blob: a4fe4149fe3404da07b24ed4682a0dff797b80cf [file] [log] [blame]
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Apache Dubbo – 生态</title><link>https://dubbo.apache.org/zh-cn/tags/%E7%94%9F%E6%80%81/</link><description>Recent content in 生态 on Apache Dubbo</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Sat, 01 Apr 2023 00:00:00 +0000</lastBuildDate><atom:link href="https://dubbo.apache.org/zh-cn/tags/%E7%94%9F%E6%80%81/index.xml" rel="self" type="application/rss+xml"/><item><title>Blog: 如何通过 Higress 网关代理 Dubbo 服务</title><link>https://dubbo.apache.org/zh-cn/blog/2023/04/01/%E5%A6%82%E4%BD%95%E9%80%9A%E8%BF%87-higress-%E7%BD%91%E5%85%B3%E4%BB%A3%E7%90%86-dubbo-%E6%9C%8D%E5%8A%A1/</link><pubDate>Sat, 01 Apr 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/04/01/%E5%A6%82%E4%BD%95%E9%80%9A%E8%BF%87-higress-%E7%BD%91%E5%85%B3%E4%BB%A3%E7%90%86-dubbo-%E6%9C%8D%E5%8A%A1/</guid><description>
&lt;h1 id="higress-对接-dubbo-服务">Higress 对接 Dubbo 服务&lt;/h1>
&lt;p>Higress提供了从HTTP协议到Dubbo协议进行转换的功能,用户通过配置协议转换,可以将一个Dubbo服务以HTTP接口暴露出来,从而用HTTP请求实现对Dubbo接口的调用。本文将通过一个示例来介绍如何用Higress配置HTTP到Dubbo的协议转换。该示例会引导您轻松地部署一个Nacos server和一个Dubbo服务,然后通过Ingress将HTTP请求转发到注册在Nacos上的Dubbo服务,并通过Higress的协议转换能力完成对Dubbo服务的HTTP调用。&lt;/p>
&lt;h2 id="前提条件">前提条件&lt;/h2>
&lt;ol>
&lt;li>Higress目前支持的Dubbo框架的版本为2.x。若您使用Dubbo3.0,要求使用dubbo协议(目前暂不支持Triple协议)。&lt;/li>
&lt;li>已安装Higress,并开启了对Istio CRD的支持,参考&lt;a href="https://higress.io/zh-cn/docs/ops/deploy-by-helm">Higress安装部署文档&lt;/a>。&lt;/li>
&lt;/ol>
&lt;h2 id="部署nacos和dubbo服务">部署Nacos和Dubbo服务&lt;/h2>
&lt;p>首先在K8s集群中apply以下资源,以部署一个Nacos注册中心,同时通过K8s service将这个Nacos server暴露出来。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># Nacos Server配置&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">apiVersion&lt;/span>: apps/v1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">kind&lt;/span>: Deployment
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">metadata&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: nacos-server
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">spec&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">replicas&lt;/span>: &lt;span style="color:#2aa198">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">selector&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">matchLabels&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">app&lt;/span>: nacos-server
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">template&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">metadata&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">labels&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">app&lt;/span>: nacos-server
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">spec&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">containers&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">env&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">name&lt;/span>: MODE
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">value&lt;/span>: standalone
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">image&lt;/span>: nacos/nacos-server:v2.2.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">imagePullPolicy&lt;/span>: Always
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: nacos-server
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">ports&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">containerPort&lt;/span>: &lt;span style="color:#2aa198">8848&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: server
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">dnsPolicy&lt;/span>: ClusterFirst
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">restartPolicy&lt;/span>: Always
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># Nacos Server Service配置&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>---
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">apiVersion&lt;/span>: v1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">kind&lt;/span>: Service
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">metadata&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: nacos-server
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">spec&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">ports&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">port&lt;/span>: &lt;span style="color:#2aa198">8848&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: server
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protocol&lt;/span>: TCP
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">targetPort&lt;/span>: &lt;span style="color:#2aa198">8848&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">selector&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">app&lt;/span>: nacos-server
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">type&lt;/span>: ClusterIP
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在K8s集群中apply以下资源,以部署一个Dubbo服务,该Dubbo服务将注册到上述的Naocs中。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">apiVersion&lt;/span>: apps/v1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">kind&lt;/span>: Deployment
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">metadata&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: nacos-provider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">spec&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">replicas&lt;/span>: &lt;span style="color:#2aa198">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">selector&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">matchLabels&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">app&lt;/span>: nacos-provider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">template&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">metadata&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">labels&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">app&lt;/span>: nacos-provider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">spec&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">containers&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">name&lt;/span>: server
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">image&lt;/span>: higress-registry.cn-hangzhou.cr.aliyuncs.com/samples/nacos-dubbo-provider:v1.0.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">imagePullPolicy&lt;/span>: IfNotPresent
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">ports&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">containerPort&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">env&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">name&lt;/span>: DUBBO_REGISTRY_ADDRESS
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">value&lt;/span>: nacos-server.default.svc.cluster.local
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>该Dubbo服务的代码可以在Nacos的&lt;a href="https://github.com/nacos-group/nacos-examples/tree/master/nacos-dubbo-example">示例代码&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:#719e07">package&lt;/span> com.alibaba.nacos.example.dubbo.service&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/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> String &lt;span style="color:#268bd2">sayName&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:#719e07">package&lt;/span> com.alibaba.nacos.example.dubbo.service&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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.rpc.RpcContext&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.springframework.beans.factory.annotation.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>&lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Default {@link DemoService}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * https://nacos.io/zh-cn/docs/use-nacos-with-dubbo.html
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @since 2.6.5
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Service&lt;/span>&lt;span style="color:#719e07">(&lt;/span>version &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;${demo.service.version}&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> group &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;${demo.service.group}&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">DefaultService&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">@Value&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;${demo.service.name}&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> String serviceName&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/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">sayName&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> RpcContext rpcContext &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:#719e07">return&lt;/span> String&lt;span style="color:#719e07">.&lt;/span>format&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Service [name :%s , port : %d] %s(\&amp;#34;%s\&amp;#34;) : Hello,%s&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceName&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> rpcContext&lt;span style="color:#719e07">.&lt;/span>getLocalPort&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>getMethodName&lt;span style="color:#719e07">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&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>在本示例中,该Dubbo服务的服务名为com.alibaba.nacos.example.dubbo.service.DemoService,服务版本为1.0.0,服务分组为dev。&lt;/p>
&lt;p>为了测试方便,我们可以通过运行以下命令来将我们部署在K8s集群中的Naocs服务映射到本地端口:&lt;/p>
&lt;div class="highlight">&lt;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>kubectl port-forward svc/nacos-server 8848:8848 --address&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#39;0.0.0.0&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>然后请求Nacos的服务发现接口,可以查看到我们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">$curl&lt;/span> -X GET &lt;span style="color:#2aa198">&amp;#39;http://127.0.0.1:8848/nacos/v1/ns/instance/list?serviceName=providers:com.alibaba.nacos.example.dubbo.service.DemoService:1.0.0:dev&amp;#39;&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;name&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;DEFAULT_GROUP@@providers:com.alibaba.nacos.example.dubbo.service.DemoService:1.0.0:dev&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;groupName&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;DEFAULT_GROUP&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;clusters&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;cacheMillis&amp;#34;&lt;/span>:10000,&lt;span style="color:#2aa198">&amp;#34;hosts&amp;#34;&lt;/span>:&lt;span style="color:#719e07">[{&lt;/span>&lt;span style="color:#2aa198">&amp;#34;ip&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;10.244.0.58&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;port&amp;#34;&lt;/span>:20880,&lt;span style="color:#2aa198">&amp;#34;weight&amp;#34;&lt;/span>:1.0,&lt;span style="color:#2aa198">&amp;#34;healthy&amp;#34;&lt;/span>:true,&lt;span style="color:#2aa198">&amp;#34;enabled&amp;#34;&lt;/span>:true,&lt;span style="color:#2aa198">&amp;#34;ephemeral&amp;#34;&lt;/span>:true,&lt;span style="color:#2aa198">&amp;#34;clusterName&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;DEFAULT&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;serviceName&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;DEFAULT_GROUP@@providers:com.alibaba.nacos.example.dubbo.service.DemoService:1.0.0:dev&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;metadata&amp;#34;&lt;/span>:&lt;span style="color:#719e07">{&lt;/span>&lt;span style="color:#2aa198">&amp;#34;side&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;provider&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;release&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;dubbo_demo&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;methods&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;sayName&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;deprecated&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;false&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;2.0.2&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;pid&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;3034042&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;interface&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;com.alibaba.nacos.example.dubbo.service.DemoService&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;service-name-mapping&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;version&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;1.0.0&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;generic&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;false&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;revision&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;dubbo_demo&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;path&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;com.alibaba.nacos.example.dubbo.service.DemoService&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;protocol&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;metadata-type&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;remote&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;application&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;dubbo-provider-demo&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;background&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;false&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;dynamic&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;category&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;providers&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;group&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;dev&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;anyhost&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;timestamp&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;1680176973875&amp;#34;&lt;/span>&lt;span style="color:#719e07">}&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;ipDeleteTimeout&amp;#34;&lt;/span>:30000,&lt;span style="color:#2aa198">&amp;#34;instanceHeartBeatInterval&amp;#34;&lt;/span>:5000,&lt;span style="color:#2aa198">&amp;#34;instanceHeartBeatTimeOut&amp;#34;&lt;/span>:15000&lt;span style="color:#719e07">}]&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;lastRefTime&amp;#34;&lt;/span>:1680178336936,&lt;span style="color:#2aa198">&amp;#34;checksum&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;allIPs&amp;#34;&lt;/span>:false,&lt;span style="color:#2aa198">&amp;#34;reachProtectionThreshold&amp;#34;&lt;/span>:false,&lt;span style="color:#2aa198">&amp;#34;valid&amp;#34;&lt;/span>:true&lt;span style="color:#719e07">}&lt;/span>%
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="通过ingress转发请求到dubbo服务">通过Ingress转发请求到Dubbo服务&lt;/h2>
&lt;p>Higress可以通过McpBridge来对接Nacos作为服务来源,在K8s集群中apply以下资源来配置McpBridge&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">apiVersion&lt;/span>: networking.higress.io/v1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">kind&lt;/span>: McpBridge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">metadata&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: default
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">namespace&lt;/span>: higress-system
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">spec&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">registries&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">domain&lt;/span>: nacos-server.default.svc.cluster.local
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">nacosGroups&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - DEFAULT_GROUP
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: nacos-service-resource
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">port&lt;/span>: &lt;span style="color:#2aa198">8848&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">type&lt;/span>: nacos2
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>通过McpBridge,我们可以直接从Nacos中发现Dubbo服务,并为其创建路由,而无需为每一个Dubbo服务创建service资源。&lt;/p>
&lt;p>接下来我们创建如下Ingress,从而创建一条指向Dubbo服务的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-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">apiVersion&lt;/span>: networking.k8s.io/v1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">kind&lt;/span>: Ingress
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">metadata&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">annotations&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">higress.io/destination&lt;/span>: providers:com.alibaba.nacos.example.dubbo.service.DemoService:1.0.0:dev.DEFAULT-GROUP.public.nacos
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: demo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">namespace&lt;/span>: higress-system
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">spec&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">ingressClassName&lt;/span>: higress
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">rules&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">http&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> - &lt;span style="color:#268bd2">backend&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">resource&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">apiGroup&lt;/span>: networking.higress.io
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">kind&lt;/span>: McpBridge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: default
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">path&lt;/span>: /dubbo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">pathType&lt;/span>: Prefix
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这样,path前缀为/dubbo的请求就会被路由到我们刚刚创建的Dubbo服务上。&lt;/p>
&lt;h2 id="通过envoyfilter配置http到dubbo的协议转换规则">通过EnvoyFilter配置HTTP到Dubbo的协议转换规则&lt;/h2>
&lt;p>经过上述步骤,我们已经在K8s环境下部署了一套Naocs和Dubbo,并通过Ingress将path前缀为/dubbo的请求路由到我们配好的Dubbo服务上。但光是这样是无法正常通信的,因为Dubbo服务使用的是定制的Dubbo协议,无法天然与HTTP协议进行兼容。因此接下来我们将通过EnvoyFilter来配置HTTP到Dubbo的协议转换规则,从而实现用HTTP请求来调用Dubbo服务。&lt;/p>
&lt;p>在K8s集群中apply以下资源,要注意的是,EnvoyFilter是属于Istio的CRD,因此需要参照前提条件中的第2点来开启Higress对Istio CRD的支持。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">apiVersion&lt;/span>: networking.istio.io/v1alpha3
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">kind&lt;/span>: EnvoyFilter
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">metadata&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: http-dubbo-transcoder-test
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">namespace&lt;/span>: higress-system
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">spec&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">configPatches&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">applyTo&lt;/span>: HTTP_FILTER
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">match&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">context&lt;/span>: GATEWAY
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">listener&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">filterChain&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">filter&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: envoy.filters.network.http_connection_manager
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">subFilter&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: envoy.filters.http.router
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">patch&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">operation&lt;/span>: INSERT_BEFORE
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">value&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: envoy.filters.http.http_dubbo_transcoder
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">typed_config&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#39;@type&amp;#39;&lt;/span>: type.googleapis.com/udpa.type.v1.TypedStruct
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">type_url&lt;/span>: type.googleapis.com/envoy.extensions.filters.http.http_dubbo_transcoder.v3.HttpDubboTranscoder
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">applyTo&lt;/span>: HTTP_ROUTE
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">match&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">context&lt;/span>: GATEWAY
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">routeConfiguration&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">vhost&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">route&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: demo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">patch&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">operation&lt;/span>: MERGE
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">value&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">route&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">upgrade_configs&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">connect_config&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">allow_post&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">upgrade_type&lt;/span>: CONNECT
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">typed_per_filter_config&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">envoy.filters.http.http_dubbo_transcoder&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#39;@type&amp;#39;&lt;/span>: type.googleapis.com/udpa.type.v1.TypedStruct
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">type_url&lt;/span>: type.googleapis.com/envoy.extensions.filters.http.http_dubbo_transcoder.v3.HttpDubboTranscoder
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">value&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">request_validation_options&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">reject_unknown_method&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">reject_unknown_query_parameters&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">services_mapping&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">group&lt;/span>: dev
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">method_mapping&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">name&lt;/span>: sayName
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">parameter_mapping&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">extract_key&lt;/span>: p
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">extract_key_spec&lt;/span>: ALL_QUERY_PARAMETER
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">mapping_type&lt;/span>: java.lang.String
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">passthrough_setting&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">passthrough_all_headers&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">path_matcher&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">match_http_method_spec&lt;/span>: ALL_GET
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">match_pattern&lt;/span>: /dubbo/hello
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: com.alibaba.nacos.example.dubbo.service.DemoService
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">version&lt;/span>: &lt;span style="color:#2aa198">1.0.0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">url_unescape_spec&lt;/span>: ALL_CHARACTERS_EXCEPT_RESERVED
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">applyTo&lt;/span>: CLUSTER
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">match&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">cluster&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">service&lt;/span>: providers:com.alibaba.nacos.example.dubbo.service.DemoService:1.0.0:dev.DEFAULT-GROUP.public.nacos
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">context&lt;/span>: GATEWAY
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">patch&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">operation&lt;/span>: MERGE
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">value&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">upstream_config&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: envoy.upstreams.http.dubbo_tcp
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">typed_config&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#39;@type&amp;#39;&lt;/span>: type.googleapis.com/udpa.type.v1.TypedStruct
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">type_url&lt;/span>: type.googleapis.com/envoy.extensions.upstreams.http.dubbo_tcp.v3.DubboTcpConnectionPoolProto
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在以上EnvoyFilter中,我们配置了将path为/dubbo/hello的HTTP请求转发到Dubbo服务com.alibaba.nacos.example.dubbo.service.DemoService:1.0.0:dev中,并调用其sayName方法,而该方法的参数则通过HTTP url中的的query参数p来指定。&lt;/p>
&lt;h2 id="请求验证">请求验证&lt;/h2>
&lt;p>通过以上配置,我们就可以执行以下curl命令来调用这个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">$curl&lt;/span> &lt;span style="color:#2aa198">&amp;#34;localhost/dubbo/hello?p=abc&amp;#34;&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;result&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;Service [name :demoService , port : 20880] sayName(\&amp;#34;abc\&amp;#34;) : Hello,abc&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>EnvoyFilter的相关配置项参考&lt;a href="https://higress.io/zh-cn/docs/user/dubbo-envoyfilter">HTTP转Dubbo配置说明&lt;/a>&lt;/p></description></item><item><title>Blog: 如何通过 Apache ShenYu 网关代理 Dubbo 服务</title><link>https://dubbo.apache.org/zh-cn/blog/2022/05/04/%E5%A6%82%E4%BD%95%E9%80%9A%E8%BF%87-apache-shenyu-%E7%BD%91%E5%85%B3%E4%BB%A3%E7%90%86-dubbo-%E6%9C%8D%E5%8A%A1/</link><pubDate>Wed, 04 May 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/05/04/%E5%A6%82%E4%BD%95%E9%80%9A%E8%BF%87-apache-shenyu-%E7%BD%91%E5%85%B3%E4%BB%A3%E7%90%86-dubbo-%E6%9C%8D%E5%8A%A1/</guid><description>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/ApacheShenYu-Dubbo-zh.png" alt="img">&lt;/p>
&lt;h2 id="1-介绍">1. 介绍&lt;/h2>
&lt;ul>
&lt;li>Apache ShenYu&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/shenyu.png" alt="img">&lt;/p>
&lt;p>&lt;a href="https://shenyu.apache.org/zh/docs/index">Apache ShenYu(Incubating)&lt;/a> 是一个异步的,高性能的,跨语言的,响应式的 &lt;code>API&lt;/code> 网关。兼容各种主流框架体系,支持热插拔,用户可以定制化开发,满足用户各种场景的现状和未来需求,经历过大规模场景的锤炼。&lt;/p>
&lt;p>2021年5月,&lt;code>ShenYu&lt;/code>捐献给 &lt;code>Apache&lt;/code> 软件基金会,Apache 基金会全票通过,顺利进入孵化器。&lt;/p>
&lt;ul>
&lt;li>Apache Dubbo&lt;/li>
&lt;/ul>
&lt;p>&lt;code>Apache Dubbo&lt;/code> 是一款微服务开发框架,它提供了 &lt;code>RPC&lt;/code> 通信 与 微服务治理 两大关键能力。这意味着,使用 &lt;code>Dubbo&lt;/code> 开发的微服务,将具备相互之间的远程发现与通信能力, 同时利用 Dubbo 提供的丰富服务治理能力,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。同时 &lt;code>Dubbo&lt;/code> 是高度可扩展的,用户几乎可以在任意功能点去定制自己的实现,以改变框架的默认行为来满足自己的业务需求。&lt;/p>
&lt;h2 id="2-dubbo快速开始">2. Dubbo快速开始&lt;/h2>
&lt;p>本小节介绍如何将&lt;code>Dubbo&lt;/code>服务接入到&lt;code>ShenYu&lt;/code>网关,您可以直接在工程下找到本小节的&lt;a href="https://github.com/apache/shenyu/tree/master/shenyu-examples/shenyu-examples-dubbo">示例代码&lt;/a> 。&lt;/p>
&lt;h3 id="21-启动shenyu-admin">2.1 启动shenyu-admin&lt;/h3>
&lt;p>&lt;code>shenyu-admin&lt;/code>是&lt;code>Apache ShenYu&lt;/code>后台管理系统, 启动的方式有多种,本文通过 &lt;code>[本地部署](https://shenyu.apache.org/zh/docs/deployment/deployment-local)&lt;/code> 的方式启动。启动成功后,需要在基础配置&lt;code>-&amp;gt;&lt;/code>插件管理中,把&lt;code>dubbo&lt;/code> 插件设置为开启,并设置你的注册地址,请确保注册中心已经开启。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/dubbo-enable-zh.png" alt="img">&lt;/p>
&lt;h3 id="22-启动shenyu网关">2.2 启动shenyu网关&lt;/h3>
&lt;p>在这里通过 &lt;a href="https://github.com/apache/incubator-shenyu/tree/master/shenyu-bootstrap">源码&lt;/a> 的方式启动,直接运行&lt;code>shenyu-bootstrap&lt;/code>中的&lt;code>ShenyuBootstrapApplication&lt;/code>。&lt;/p>
&lt;p>在启动前,请确保网关已经引入相关依赖。如果客户端是&lt;code>apache dubbo&lt;/code>,注册中心使用&lt;code>zookeeper&lt;/code>,请参考如下配置:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;!--&lt;/span> apache shenyu apache dubbo plugin start&lt;span style="color:#719e07">--&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>dependency&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>shenyu&lt;span style="color:#719e07">&amp;lt;/&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>shenyu&lt;span style="color:#719e07">-&lt;/span>spring&lt;span style="color:#719e07">-&lt;/span>boot&lt;span style="color:#719e07">-&lt;/span>starter&lt;span style="color:#719e07">-&lt;/span>plugin&lt;span style="color:#719e07">-&lt;/span>apache&lt;span style="color:#719e07">-&lt;/span>dubbo&lt;span style="color:#719e07">&amp;lt;/&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>version&lt;span style="color:#719e07">&amp;gt;&lt;/span>$&lt;span style="color:#719e07">{&lt;/span>project&lt;span style="color:#719e07">.&lt;/span>version&lt;span style="color:#719e07">}&amp;lt;/&lt;/span>version&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;/&lt;/span>dependency&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>dependency&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">&amp;lt;/&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>dubbo&lt;span style="color:#719e07">&amp;lt;/&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>version&lt;span style="color:#719e07">&amp;gt;&lt;/span>2&lt;span style="color:#719e07">.&lt;/span>7&lt;span style="color:#719e07">.&lt;/span>5&lt;span style="color:#719e07">&amp;lt;/&lt;/span>version&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;/&lt;/span>dependency&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;!--&lt;/span> Dubbo zookeeper registry dependency start &lt;span style="color:#719e07">--&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>dependency&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>curator&lt;span style="color:#719e07">&amp;lt;/&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>curator&lt;span style="color:#719e07">-&lt;/span>client&lt;span style="color:#719e07">&amp;lt;/&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>version&lt;span style="color:#719e07">&amp;gt;&lt;/span>4&lt;span style="color:#719e07">.&lt;/span>0&lt;span style="color:#719e07">.&lt;/span>1&lt;span style="color:#719e07">&amp;lt;/&lt;/span>version&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>exclusions&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>exclusion&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>log4j&lt;span style="color:#719e07">&amp;lt;/&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>log4j&lt;span style="color:#719e07">&amp;lt;/&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;/&lt;/span>exclusion&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;/&lt;/span>exclusions&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;/&lt;/span>dependency&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>dependency&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>curator&lt;span style="color:#719e07">&amp;lt;/&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>curator&lt;span style="color:#719e07">-&lt;/span>framework&lt;span style="color:#719e07">&amp;lt;/&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>version&lt;span style="color:#719e07">&amp;gt;&lt;/span>4&lt;span style="color:#719e07">.&lt;/span>0&lt;span style="color:#719e07">.&lt;/span>1&lt;span style="color:#719e07">&amp;lt;/&lt;/span>version&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;/&lt;/span>dependency&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>dependency&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>curator&lt;span style="color:#719e07">&amp;lt;/&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>curator&lt;span style="color:#719e07">-&lt;/span>recipes&lt;span style="color:#719e07">&amp;lt;/&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>version&lt;span style="color:#719e07">&amp;gt;&lt;/span>4&lt;span style="color:#719e07">.&lt;/span>0&lt;span style="color:#719e07">.&lt;/span>1&lt;span style="color:#719e07">&amp;lt;/&lt;/span>version&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;/&lt;/span>dependency&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;!--&lt;/span> Dubbo zookeeper registry dependency end &lt;span style="color:#719e07">--&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;!--&lt;/span> apache dubbo plugin end&lt;span style="color:#719e07">--&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="23-启动shenyu-examples-dubbo">2.3 启动shenyu-examples-dubbo&lt;/h3>
&lt;p>以官网提供的例子为例 &lt;a href="https://github.com/apache/shenyu/tree/master/shenyu-examples/shenyu-examples-dubbo">shenyu-examples-dubbo&lt;/a> 。 假如&lt;code>dubbo&lt;/code>服务定义如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;beans&lt;/span> /* ...... * &lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo:application&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;test-dubbo-service&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo:registry&lt;/span> address=&lt;span style="color:#2aa198">&amp;#34;${dubbo.registry.address}&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo:protocol&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span> port=&lt;span style="color:#2aa198">&amp;#34;20888&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo:service&lt;/span> timeout=&lt;span style="color:#2aa198">&amp;#34;10000&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.shenyu.examples.dubbo.api.service.DubboTestService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;dubboTestService&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/beans&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>声明应用服务名称,注册中心地址,使用&lt;code>dubbo&lt;/code>协议,声明服务接口,对应接口实现类:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * DubboTestServiceImpl.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Service&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubboTestService&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DubboTestServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> DubboTestService &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@ShenyuDubboClient&lt;/span>&lt;span style="color:#719e07">(&lt;/span>path &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;/findById&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> desc &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;Query by Id&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> DubboTest &lt;span style="color:#268bd2">findById&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#268bd2">final&lt;/span> String id&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> DubboTest&lt;span style="color:#719e07">(&lt;/span>id&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;hello world shenyu Apache, findById&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//......
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在接口实现类中,使用注解&lt;code>@ShenyuDubboClient&lt;/code>向&lt;code>shenyu-admin&lt;/code>注册服务。&lt;/p>
&lt;p>在配置文件&lt;code>application.yml&lt;/code>中的配置信息:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">server&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">port&lt;/span>: &lt;span style="color:#2aa198">8011&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">address&lt;/span>: &lt;span style="color:#2aa198">0.0.0.0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">servlet&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">context-path&lt;/span>: /
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">spring&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">main&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">allow-bean-definition-overriding&lt;/span>: &lt;span style="color:#cb4b16">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">dubbo&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">registry&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">address&lt;/span>: zookeeper://localhost:2181 &lt;span style="color:#586e75"># dubbo使用的注册中心&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">shenyu&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">register&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">registerType&lt;/span>: http &lt;span style="color:#586e75">#注册方式&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">serverLists&lt;/span>: http://localhost:9095 &lt;span style="color:#586e75">#注册地址&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">props&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">username&lt;/span>: admin
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">password&lt;/span>: &lt;span style="color:#2aa198">123456&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">client&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">dubbo&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">props&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">contextPath&lt;/span>: /dubbo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">appName&lt;/span>: dubbo
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在配置文件中,声明&lt;code>dubbo&lt;/code>使用的注册中心地址,&lt;code>dubbo&lt;/code>服务向&lt;code>shenyu-admin&lt;/code>注册,使用的方式是&lt;code>http&lt;/code>,注册地址是&lt;code>http://localhost:9095&lt;/code>。&lt;/p>
&lt;p>关于注册方式的使用,请参考 &lt;code>[应用客户端接入](https://shenyu.apache.org/docs/design/register-center-design/)&lt;/code> 。&lt;/p>
&lt;h3 id="24-调用dubbo服务">2.4 调用dubbo服务&lt;/h3>
&lt;p>&lt;code>shenyu-examples-dubbo&lt;/code>项目成功启动之后会自动把加 &lt;code>@ShenyuDubboClient&lt;/code> 注解的接口方法注册到网关。&lt;/p>
&lt;p>打开 &lt;code>插件列表 -&amp;gt; Proxy -&amp;gt; dubbo&lt;/code> 可以看到插件规则配置列表:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/dubbo-service-list-zh.png" alt="img">&lt;/p>
&lt;p>注册成功的选择器信息:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/dubbo-selector-zh.png" alt="img">&lt;/p>
&lt;p>注册成功的规则信息:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/dubbo-rule-zh.png" alt="img">&lt;/p>
&lt;blockquote>
&lt;p>选择器和规则是 &lt;code>Apache ShenYu&lt;/code> 网关中最灵魂的东西。掌握好它,你可以对任何流量进行管理。对应为选择器与规则里面的匹配条件(conditions),根据不同的流量筛选规则,我们可以处理各种复杂的场景。流量筛选可以从&lt;code>Header&lt;/code>, &lt;code>URI&lt;/code>, &lt;code>Query&lt;/code>, &lt;code>Cookie&lt;/code> 等等Http请求获取数据。&lt;/p>
&lt;p>然后可以采用 &lt;code>Match&lt;/code>,&lt;code>=&lt;/code>,&lt;code>Regex&lt;/code>,&lt;code>Groovy&lt;/code>,&lt;code>Exclude&lt;/code>等匹配方式,匹配出你所预想的数据。多组匹配添加可以使用&lt;code>And/Or&lt;/code>的匹配策略。&lt;/p>
&lt;p>具体的介绍与使用请看: &lt;code>[选择器与规则管理](https://shenyu.apache.org/zh/docs/user-guide/admin-usage/selector-and-rule)&lt;/code> 。&lt;/p>
&lt;/blockquote>
&lt;p>发起&lt;code>GET&lt;/code>请求,通过&lt;code>ShenYu&lt;/code>网关调用&lt;code>dubbo&lt;/code>服务:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>GET http://localhost:9195/dubbo/findById?id=100
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Accept: application/json
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>成功响应之后,结果如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;#34;name&amp;#34;: &amp;#34;hello world shenyu Apache, findById&amp;#34;,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;#34;id&amp;#34;: &amp;#34;100&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>至此,就成功的通过&lt;code>http&lt;/code>请求访问&lt;code>dubbo&lt;/code>服务了,&lt;code>ShenYu&lt;/code>网关通过&lt;code>shenyu-plugin-dubbo&lt;/code>模块将&lt;code>http&lt;/code>协议转成了&lt;code>dubbo&lt;/code>协议。&lt;/p>
&lt;h2 id="3-深入理解dubbo插件">3. 深入理解Dubbo插件&lt;/h2>
&lt;p>在运行上述&lt;code>demo&lt;/code>的过程中,是否存在一些疑问:&lt;/p>
&lt;ul>
&lt;li>&lt;code>dubbo&lt;/code>服务是如何注册到&lt;code>shenyu-admin&lt;/code>?&lt;/li>
&lt;li>&lt;code>shenyu-admin&lt;/code>是如何将数据同步到&lt;code>ShenYu&lt;/code>网关?&lt;/li>
&lt;li>&lt;code>DubboPlugin&lt;/code>是如何将&lt;code>http&lt;/code>协议转换到到dubbo协议?&lt;/li>
&lt;/ul>
&lt;p>带着这些疑问,来深入理解&lt;code>dubbo&lt;/code>插件。&lt;/p>
&lt;h3 id="31-应用客户端接入">3.1 应用客户端接入&lt;/h3>
&lt;p>应用客户端接入是指将微服务接入到&lt;code>Apache ShenYu&lt;/code>网关,当前支持&lt;code>Http&lt;/code>、 &lt;code>Dubbo&lt;/code>、 &lt;code>Spring Cloud&lt;/code>、 &lt;code>gRPC&lt;/code>、 &lt;code>Motan&lt;/code>、 &lt;code>Sofa&lt;/code>、 &lt;code>Tars&lt;/code>等协议的接入。&lt;/p>
&lt;p>将应用客户端接入到&lt;code>Apache ShenYu&lt;/code>网关是通过注册中心来实现的,涉及到客户端注册和服务端同步数据。注册中心支持&lt;code>Http&lt;/code>、&lt;code>Zookeeper&lt;/code>、&lt;code>Etcd&lt;/code>、&lt;code>Consul&lt;/code>和&lt;code>Nacos&lt;/code>。默认是通过&lt;code>Http&lt;/code>方式注册。&lt;/p>
&lt;p>客户端接入的相关配置请参考 &lt;code>[客户端接入配置](https://shenyu.apache.org/zh/docs/user-guide/register-center-access)&lt;/code> 。&lt;/p>
&lt;h4 id="311-客户端注册">3.1.1 客户端注册&lt;/h4>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/register-client.png" alt="img">&lt;/p>
&lt;p>在你的微服务配置中声明注册中心客户端类型,如&lt;code>Http&lt;/code>或&lt;code>Zookeeper&lt;/code>。
应用程序启动时使用&lt;code>SPI&lt;/code>方式加载并初始化对应注册中心客户端,通过实现&lt;code>Spring Bean&lt;/code>相关的后置处理器接口,在其中获取需要进行注册的服务接口信息,将获取的信息放入&lt;code>Disruptor&lt;/code>中。&lt;/p>
&lt;p>注册中心客户端从&lt;code>Disruptor&lt;/code>中读取数据,并将接口信息注册到&lt;code>shenyu-admin&lt;/code>,&lt;code>Disruptor&lt;/code>在其中起数据与操作解耦的作用,利于扩展。&lt;/p>
&lt;h4 id="312-服务端注册">3.1.2 服务端注册&lt;/h4>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/register-server.png" alt="img">&lt;/p>
&lt;p>在&lt;code>shenyu-admin&lt;/code>配置中声明注册中心服务端类型,如&lt;code>Http&lt;/code>或&lt;code>Zookeeper&lt;/code>。当&lt;code>shenyu-admin&lt;/code>启动时,读取配置类型,加载并初始化对应的注册中心服务端,注册中心服务端收到&lt;code>shenyu-client&lt;/code>注册的接口信息后,将其放入&lt;code>Disruptor&lt;/code>中,然后会触发注册处理逻辑,将服务接口信息更新并发布同步事件。&lt;/p>
&lt;p>&lt;code>Disruptor&lt;/code>在其中起到数据与操作解耦,利于扩展。如果注册请求过多,导致注册异常,也有数据缓冲作用。&lt;/p>
&lt;h3 id="32-数据同步原理">3.2 数据同步原理&lt;/h3>
&lt;p>数据同步是指在 &lt;code>shenyu-admin&lt;/code> 后台操作数据以后,使用何种策略将数据同步到 &lt;code>Apache ShenYu&lt;/code> 网关。&lt;code>Apache ShenYu&lt;/code> 网关当前支持&lt;code>ZooKeeper&lt;/code>、&lt;code>WebSocket&lt;/code>、&lt;code>Http长轮询&lt;/code>、&lt;code>Nacos&lt;/code> 、&lt;code>Etcd&lt;/code> 和 &lt;code>Consul&lt;/code> 进行数据同步。默认是通过&lt;code>WebSocket&lt;/code>进行数据同步。&lt;/p>
&lt;p>数据同步的相关配置请参考 &lt;code>[数据同步配置](https://shenyu.apache.org/zh/docs/user-guide/use-data-sync)&lt;/code> 。&lt;/p>
&lt;h4 id="321-数据同步的意义">3.2.1 数据同步的意义&lt;/h4>
&lt;p>网关是流量请求的入口,在微服务架构中承担了非常重要的角色,网关高可用的重要性不言而喻。在使用网关的过程中,为了满足业务诉求,经常需要变更配置,比如流控规则、路由规则等等。因此,网关动态配置是保障网关高可用的重要因素。&lt;/p>
&lt;p>当前数据同步特性如下:&lt;/p>
&lt;ul>
&lt;li>所有的配置都缓存在 &lt;code>Apache ShenYu&lt;/code> 网关内存中,每次请求都使用本地缓存,速度非常快。&lt;/li>
&lt;li>用户可以在 &lt;code>shenyu-admin&lt;/code> 后台任意修改数据,并马上同步到网关内存。&lt;/li>
&lt;li>支持 &lt;code>Apache ShenYu&lt;/code> 的插件、选择器、规则数据、元数据、签名数据等数据同步。&lt;/li>
&lt;li>所有插件的选择器,规则都是动态配置,立即生效,不需要重启服务。&lt;/li>
&lt;li>数据同步方式支持 &lt;code>Zookeeper&lt;/code>、&lt;code>Http 长轮询&lt;/code>、&lt;code>Websocket&lt;/code>、&lt;code>Nacos&lt;/code>、&lt;code>Etcd&lt;/code> 和 &lt;code>Consul&lt;/code>。&lt;/li>
&lt;/ul>
&lt;h4 id="322-数据同步原理分析">3.2.2 数据同步原理分析&lt;/h4>
&lt;p>下图展示了 &lt;code>Apache ShenYu&lt;/code> 数据同步的流程,&lt;code>Apache ShenYu&lt;/code> 网关在启动时,会从配置服务同步配置数据,并且支持推拉模式获取配置变更信息,然后更新本地缓存。管理员可以在管理后台(&lt;code>shenyu-admin&lt;/code>),变更用户权限、规则、插件、流量配置,通过推拉模式将变更信息同步给 &lt;code>Apache ShenYu&lt;/code> 网关,具体是 &lt;code>push&lt;/code> 模式,还是 &lt;code>pull&lt;/code> 模式取决于使用哪种同步方式。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/data-sync.png" alt="img">&lt;/p>
&lt;p>在最初的版本中,配置服务依赖 &lt;code>Zookeeper&lt;/code> 实现,管理后台将变更信息 &lt;code>push&lt;/code> 给网关。而现在可以支持 &lt;code>WebSocket&lt;/code>、&lt;code>Http长轮询&lt;/code>、&lt;code>Zookeeper&lt;/code>、&lt;code>Nacos&lt;/code>、&lt;code>Etcd&lt;/code> 和 &lt;code>Consul&lt;/code>,通过在配置文件中设置 &lt;code>shenyu.sync.${strategy}&lt;/code> 指定对应的同步策略,默认使用 &lt;code>webosocket&lt;/code> 同步策略,可以做到秒级数据同步。但是,有一点需要注意的是,&lt;code>Apache ShenYu&lt;/code>网关 和 &lt;code>shenyu-admin&lt;/code> 必须使用相同的同步策略。&lt;/p>
&lt;p>如下图所示,&lt;code>shenyu-admin&lt;/code> 在用户发生配置变更之后,会通过 &lt;code>EventPublisher&lt;/code> 发出配置变更通知,由 &lt;code>EventDispatcher&lt;/code> 处理该变更通知,然后根据配置的同步策略(&lt;code>http、weboscket、zookeeper、naocs、etcd、consul&lt;/code>),将配置发送给对应的事件处理器。&lt;/p>
&lt;ul>
&lt;li>如果是 &lt;code>websocket&lt;/code> 同步策略,则将变更后的数据主动推送给 &lt;code>shenyu-web&lt;/code>,并且在网关层,会有对应的 &lt;code>WebsocketDataHandler&lt;/code> 处理器来处理 &lt;code>shenyu-admin&lt;/code> 的数据推送。&lt;/li>
&lt;li>如果是 &lt;code>zookeeper&lt;/code> 同步策略,将变更数据更新到 &lt;code>zookeeper&lt;/code>,而 &lt;code>ZookeeperSyncCache&lt;/code> 会监听到 &lt;code>zookeeper&lt;/code> 的数据变更,并予以处理。&lt;/li>
&lt;li>如果是 &lt;code>http&lt;/code> 同步策略,由网关主动发起长轮询请求,默认有 &lt;code>90s&lt;/code> 超时时间,如果 &lt;code>shenyu-admin&lt;/code> 没有数据变更,则会阻塞 &lt;code>http&lt;/code> 请求,如果有数据发生变更则响应变更的数据信息,如果超过 &lt;code>60s&lt;/code> 仍然没有数据变更则响应空数据,网关层接到响应后,继续发起 &lt;code>http&lt;/code> 请求,反复同样的请求。&lt;/li>
&lt;/ul>
&lt;h3 id="33-流程分析">3.3 流程分析&lt;/h3>
&lt;p>流程分析是从源码的角度,展示服务注册流程,数据同步流程和服务调用流程。&lt;/p>
&lt;h4 id="331-服务注册流程">3.3.1 服务注册流程&lt;/h4>
&lt;ul>
&lt;li>读取dubbo服务&lt;/li>
&lt;/ul>
&lt;p>使用注解&lt;code>@ShenyuDubboClient&lt;/code>标记需要注册到网关的&lt;code>dubbo&lt;/code>服务。&lt;/p>
&lt;p>注解扫描通过&lt;code>ApacheDubboServiceBeanListener&lt;/code>完成,它实现了&lt;code>ApplicationListener&amp;lt;ContextRefreshedEvent&amp;gt;&lt;/code>接口,在&lt;code>Spring&lt;/code>容器启动过程中,发生上下文刷新事件时,开始执行事件处理方法&lt;code>onApplicationEvent()&lt;/code>。在重写的方法逻辑中,读取&lt;code>Dubbo&lt;/code>服务&lt;code>ServiceBean&lt;/code>,构建元数据对象和&lt;code>URI&lt;/code>对象,并向&lt;code>shenyu-admin&lt;/code>注册。&lt;/p>
&lt;p>具体的注册逻辑由注册中心实现,请参考 &lt;code>[客户端接入原理](https://shenyu.apache.org/zh/docs/design/register-center-design/)&lt;/code> 。&lt;/p>
&lt;ul>
&lt;li>处理注册信息&lt;/li>
&lt;/ul>
&lt;p>客户端通过注册中心注册的元数据和&lt;code>URI&lt;/code>数据,在&lt;code>shenyu-admin&lt;/code>端进行处理,负责存储到数据库和同步给&lt;code>shenyu&lt;/code>网关。&lt;code>Dubbo&lt;/code>插件的客户端注册处理逻辑在&lt;code>ShenyuClientRegisterDubboServiceImpl&lt;/code>中。继承关系如下:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/ShenyuClientRegisterDubboServiceImpl.png" alt="img">&lt;/p>
&lt;ul>
&lt;li>ShenyuClientRegisterService:客户端注册服务,顶层接口;&lt;/li>
&lt;li>FallbackShenyuClientRegisterService:注册失败,提供重试操作;&lt;/li>
&lt;li>AbstractShenyuClientRegisterServiceImpl:抽象类,实现部分公共注册逻辑;&lt;/li>
&lt;li>ShenyuClientRegisterDubboServiceImpl:实现&lt;code>Dubbo&lt;/code>插件的注册;&lt;/li>
&lt;/ul>
&lt;p>注册信息包括选择器,规则和元数据。&lt;/p>
&lt;p>整体的&lt;code>dubbo&lt;/code>服务注册流程如下:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/dubbo-register-zh.png" alt="img">&lt;/p>
&lt;h4 id="332-数据同步流程">3.3.2 数据同步流程&lt;/h4>
&lt;ul>
&lt;li>admin更新数据&lt;/li>
&lt;/ul>
&lt;p>假设在在后台管理系统中,新增一条选择器数据,请求会进入&lt;code>SelectorController&lt;/code>类中的&lt;code>createSelector()&lt;/code>方法,它负责数据的校验,添加或更新数据,返回结果信息。&lt;/p>
&lt;p>在&lt;code>SelectorServiceImpl&lt;/code>类中通过&lt;code>createOrUpdate()&lt;/code>方法完成数据的转换,保存到数据库,发布事件,更新&lt;code>upstream&lt;/code>。&lt;/p>
&lt;p>在&lt;code>Service&lt;/code>类完成数据的持久化操作,即保存数据到数据库。发布变更数据通过&lt;code>eventPublisher.publishEvent()&lt;/code>完成,这个&lt;code>eventPublisher&lt;/code>对象是一个&lt;code>ApplicationEventPublisher&lt;/code>类,这个类的全限定名是&lt;code>org.springframework.context.ApplicationEventPublisher&lt;/code>,发布数据的功能正是是通过&lt;code>Spring&lt;/code>相关的功能来完成的。&lt;/p>
&lt;p>当事件发布完成后,会自动进入到&lt;code>DataChangedEventDispatcher&lt;/code>类中的&lt;code>onApplicationEvent()&lt;/code>方法,根据不同数据类型和数据同步方式进行事件处理。&lt;/p>
&lt;ul>
&lt;li>网关数据同步&lt;/li>
&lt;/ul>
&lt;p>网关在启动时,根据指定的数据同步方式加载不同的配置类,初始化数据同步相关类。&lt;/p>
&lt;p>在接收到数据后,进行反序列化操作,读取数据类型和操作类型。不同的数据类型,有不同的数据处理方式,所以有不同的实现类。但是它们之间也有相同的处理逻辑,所以可以通过模板方法设计模式来实现。相同的逻辑放在抽象类&lt;code>AbstractDataHandler&lt;/code>中的&lt;code>handle()&lt;/code>方法中,不同逻辑就交给各自的实现类。&lt;/p>
&lt;p>新增一条选择器数据,是新增操作,会进入到&lt;code>SelectorDataHandler.doUpdate()&lt;/code>具体的数据处理逻辑中。&lt;/p>
&lt;p>在通用插件数据订阅者&lt;code>CommonPluginDataSubscriber&lt;/code>,负责处理所有插件、选择器和规则信息&lt;/p>
&lt;p>将数据保存到网关的内存中,&lt;code>BaseDataCache&lt;/code>是最终缓存数据的类,通过单例模式实现。选择器数据就存到了&lt;code>SELECTOR_MAP&lt;/code>这个&lt;code>Map&lt;/code>中。在后续使用的时候,也是从这里拿数据。&lt;/p>
&lt;p>上述逻辑用流程图表示如下:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/data-sync-seq-zh.png" alt="img">&lt;/p>
&lt;h4 id="333-服务调用流程">3.3.3 服务调用流程&lt;/h4>
&lt;p>在&lt;code>Dubbo&lt;/code>插件体系中,类继承关系如下:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/ApacheDubboPlugin.png" alt="img">&lt;/p>
&lt;blockquote>
&lt;p>ShenyuPlugin:顶层接口,定义接口方法;&lt;/p>
&lt;p>AbstractShenyuPlugin:抽象类,实现插件共有逻辑;&lt;/p>
&lt;p>AbstractDubboPlugin:dubbo插件抽象类,实现&lt;code>dubbo&lt;/code>共有逻辑(ShenYu网关支持ApacheDubbo和AlibabaDubbo);&lt;/p>
&lt;p>ApacheDubboPlugin:ApacheDubbo插件。&lt;/p>
&lt;/blockquote>
&lt;ul>
&lt;li>org.apache.shenyu.web.handler.ShenyuWebHandler.DefaultShenyuPluginChain#execute()&lt;/li>
&lt;/ul>
&lt;p>通过&lt;code>ShenYu&lt;/code>网关代理后,请求入口是&lt;code>ShenyuWebHandler&lt;/code>,它实现了&lt;code>org.springframework.web.server.WebHandler&lt;/code>接口,通过责任链设计模式将所有插件连接起来。&lt;/p>
&lt;ul>
&lt;li>org.apache.shenyu.plugin.base.AbstractShenyuPlugin#execute()&lt;/li>
&lt;/ul>
&lt;p>当请求到网关时,判断某个插件是否执行,是通过指定的匹配逻辑来完成。在&lt;code>execute()&lt;/code>方法中执行选择器和规则的匹配逻辑。&lt;/p>
&lt;ul>
&lt;li>org.apache.shenyu.plugin.global.GlobalPlugin#execute()&lt;/li>
&lt;/ul>
&lt;p>最先被执行的是&lt;code>GlobalPlugin&lt;/code> ,它是一个全局插件,在&lt;code>execute()&lt;/code>方法中构建上下文信息。&lt;/p>
&lt;ul>
&lt;li>org.apache.shenyu.plugin.base.RpcParamTransformPlugin#execute()&lt;/li>
&lt;/ul>
&lt;p>接着被执行的是&lt;code>RpcParamTransformPlugin&lt;/code> , 它负责从&lt;code>http&lt;/code>请求中读取参数,保存到&lt;code>exchange&lt;/code>中,传递给&lt;code>rpc&lt;/code>服务。在&lt;code>execute()&lt;/code>方法中,执行该插件的核心逻辑:从&lt;code>exchange&lt;/code>中获取请求信息,根据请求传入的内容形式处理参数。&lt;/p>
&lt;ul>
&lt;li>org.apache.shenyu.plugin.dubbo.common.AbstractDubboPlugin&lt;/li>
&lt;/ul>
&lt;p>然后被执行的是&lt;code>DubboPlugin&lt;/code> 。在&lt;code>doExecute()&lt;/code>方法中,主要是检查元数据和参数。在&lt;code>doDubboInvoker()&lt;/code>方法中设置特殊的上下文信息,然后开始&lt;code>dubbo&lt;/code>的泛化调用。&lt;/p>
&lt;p>在&lt;code>genericInvoker()&lt;/code>方法中:&lt;/p>
&lt;ul>
&lt;li>获取&lt;code>ReferenceConfig&lt;/code>对象;&lt;/li>
&lt;li>获取泛化服务&lt;code>GenericService&lt;/code>对象;&lt;/li>
&lt;li>构造请求参数&lt;code>pair&lt;/code>对象;&lt;/li>
&lt;li>发起异步的泛化调用。&lt;/li>
&lt;/ul>
&lt;p>通过泛化调用就可以实现在网关调用&lt;code>dubbo&lt;/code>服务了。&lt;/p>
&lt;p>&lt;code>ReferenceConfig&lt;/code>对象是支持泛化调用的关键对象 ,它的初始化操作是在数据同步的时候完成的。&lt;/p>
&lt;ul>
&lt;li>org.apache.shenyu.plugin.response.ResponsePlugin#execute()&lt;/li>
&lt;/ul>
&lt;p>最后被执行的是&lt;code>ResponsePlugin&lt;/code> ,它统一处理网关的响应结果信息。处理类型由&lt;code>MessageWriter&lt;/code>决定,类继承关系如下:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/MessageWriter.png" alt="img">&lt;/p>
&lt;blockquote>
&lt;p>MessageWriter:接口,定义消息处理方法;&lt;/p>
&lt;p>NettyClientMessageWriter:处理&lt;code>Netty&lt;/code>调用结果;&lt;/p>
&lt;p>RPCMessageWriter:处理&lt;code>RPC&lt;/code>调用结果;&lt;/p>
&lt;p>WebClientMessageWriter:处理&lt;code>WebClient&lt;/code>调用结果;&lt;/p>
&lt;/blockquote>
&lt;p>&lt;code>Dubbo&lt;/code>服务调用,处理结果是&lt;code>RPCMessageWriter&lt;/code>。&lt;/p>
&lt;ul>
&lt;li>org.apache.shenyu.plugin.response.strategy.RPCMessageWriter#writeWith()&lt;/li>
&lt;/ul>
&lt;p>在&lt;code>writeWith()&lt;/code>方法中处理响应结果,获取结果或处理异常。&lt;/p>
&lt;p>分析至此,关于&lt;code>Dubbo&lt;/code>插件的源码分析就完成了,分析流程图如下:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/dubbo-execute-zh.png" alt="img">&lt;/p>
&lt;h2 id="4-小结">4. 小结&lt;/h2>
&lt;p>本文从实际案例出发,由浅入深分析了&lt;code>ShenYu&lt;/code>网关对Dubbo服务的代理过程。涉及到的主要知识点如下:&lt;/p>
&lt;ul>
&lt;li>通过责任链设计模式执行插件;&lt;/li>
&lt;li>使用模板方法设计模式实现&lt;code>AbstractShenyuPlugin&lt;/code>,处理通用的操作类型;&lt;/li>
&lt;li>使用单例设计模式实现缓存数据类&lt;code>BaseDataCache&lt;/code>;&lt;/li>
&lt;li>通过&lt;code>springboot starter&lt;/code>即可引入不同的注册中心和数同步方式,扩展性很好;&lt;/li>
&lt;li>通过&lt;code>admin&lt;/code>支持规则热更新,方便流量管控;&lt;/li>
&lt;li>&lt;code>Disruptor&lt;/code>队列是为了数据与操作解耦,以及数据缓冲。&lt;/li>
&lt;/ul></description></item><item><title>Blog: 从原理到操作,让你在 Apache APISIX 中代理 Dubbo 服务更便捷</title><link>https://dubbo.apache.org/zh-cn/blog/2022/01/18/%E4%BB%8E%E5%8E%9F%E7%90%86%E5%88%B0%E6%93%8D%E4%BD%9C%E8%AE%A9%E4%BD%A0%E5%9C%A8-apache-apisix-%E4%B8%AD%E4%BB%A3%E7%90%86-dubbo-%E6%9C%8D%E5%8A%A1%E6%9B%B4%E4%BE%BF%E6%8D%B7/</link><pubDate>Tue, 18 Jan 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/01/18/%E4%BB%8E%E5%8E%9F%E7%90%86%E5%88%B0%E6%93%8D%E4%BD%9C%E8%AE%A9%E4%BD%A0%E5%9C%A8-apache-apisix-%E4%B8%AD%E4%BB%A3%E7%90%86-dubbo-%E6%9C%8D%E5%8A%A1%E6%9B%B4%E4%BE%BF%E6%8D%B7/</guid><description>
&lt;h2 id="背景">背景&lt;/h2>
&lt;p>&lt;a href="https://dubbo.apache.org/zh-cn/">Apache Dubbo&lt;/a> 是由阿里巴巴开源并捐赠给 Apache 的微服务开发框架,它提供了 RPC 通信与微服务治理两大关键能力。不仅经过了阿里电商场景中海量流量的验证,也在国内的技术公司中被广泛落地。&lt;/p>
&lt;p>在实际应用场景中,Apache Dubbo 一般会作为后端系统间 RPC 调用的实现框架,当需要提供 HTTP 接口给到前端时,会通过一个「胶水层」将 Dubbo Service 包装成 HTTP 接口,再交付到前端系统。&lt;/p>
&lt;p>&lt;a href="https://apisix.apache.org/">Apache APISIX&lt;/a> 是 Apache 软件基金会的顶级开源项目,也是当前最活跃的开源网关项目。作为一个动态、实时、高性能的开源 API 网关,Apache APISIX 提供了负载均衡、动态上游、灰度发布、服务熔断、身份认证、可观测性等丰富的流量管理功能。&lt;/p>
&lt;p>得益于 Apache Dubbo 的应用场景优势,Apache APISIX 基于开源项目 tengine/mod_dubbo 模块为 Apache Dubbo 服务配备了HTTP 网关能力。通过 dubbo-proxy 插件,可以轻松地将 Dubbo Service 发布为 HTTP 服务。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/apisix-plugin/1.png" alt="架构图">&lt;/p>
&lt;h2 id="如何使用">如何使用&lt;/h2>
&lt;h3 id="入门篇安装使用">入门篇:安装使用&lt;/h3>
&lt;blockquote>
&lt;p>这里我们建议使用 Apache APISIX 2.11 版本镜像进行安装。该版本的 APISIX-Base 中已默认编译了 Dubbo 模块,可直接使用 &lt;code>dubbo-proxy&lt;/code> 插件。&lt;/p>
&lt;/blockquote>
&lt;p>在接下来的操作中,我们将使用 &lt;a href="https://github.com/apache/dubbo-samples">&lt;code>dubbo-samples&lt;/code>&lt;/a> 项目进行部分展示。该项目是一些使用 Apache Dubbo 实现的 Demo 应用,本文中我们采用其中的一个子模块作为 Dubbo Provider。&lt;/p>
&lt;p>在进入正式操作前,我们先简单看下 Dubbo 接口的定义、配置以及相关实现。&lt;/p>
&lt;h4 id="接口实现一览">接口实现一览&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">DemoService&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * standard samples dubbo infterace demo
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param context pass http infos
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @return Map&amp;lt;String, Object&amp;gt;&amp;lt;/&amp;gt; pass to response http
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> **/&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">apisixDubbo&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> httpRequestContext&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如上所示,Dubbo 接口的定义是固定的。即方法参数中 &lt;code>Map&lt;/code> 表示 APISIX 传递给 Dubbo Provider 关于 HTTP request 的一些信息(如:header、body&amp;hellip;)。而方法返回值的 &lt;code>Map&lt;/code> 表示 Dubbo Provider 传递给 APISIX 要如何返回 HTTP response 的一些信息。&lt;/p>
&lt;p>接口信息配置好之后可通过 XML 配置方式发布 DemoService。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&amp;lt;!-- service implementation, as same as regular local bean --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;bean&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span> class=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.provider.DemoServiceImpl&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&amp;lt;!-- declare the service interface to be exported --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:service&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.apisix.DemoService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>通过上述配置后,Consumer 可通过 &lt;code>org.apache.dubbo.samples.apisix.DemoService&lt;/code> 访问其中的&lt;code>apisixDubbo&lt;/code> 方法。具体接口实现如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DemoServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> DemoService &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">apisixDubbo&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> httpRequestContext&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">.&lt;/span>Entry&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> entry &lt;span style="color:#719e07">:&lt;/span> httpRequestContext&lt;span style="color:#719e07">.&lt;/span>entrySet&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Key = &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> entry&lt;span style="color:#719e07">.&lt;/span>getKey&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, Value = &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> entry&lt;span style="color:#719e07">.&lt;/span>getValue&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> ret &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> HashMap&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ret&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;body&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;dubbo success\n&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// http response body
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ret&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;status&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;200&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// http response status
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ret&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;test&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;123&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// http response header
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> ret&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>上述代码中,&lt;code>DemoServiceImpl&lt;/code> 会打印接收到的 &lt;code>httpRequestContext&lt;/code>,并通过返回包含有指定 Key 的 Map 对象去描述该 Dubbo 请求的 HTTP 响应。&lt;/p>
&lt;h4 id="操作步骤">操作步骤&lt;/h4>
&lt;ol>
&lt;li>启动 &lt;a href="https://github.com/apache/dubbo-samples/tree/master/2-advanced/dubbo-samples-tengine#install-dubbo">&lt;code>dubbo-samples&lt;/code>&lt;/a>。&lt;/li>
&lt;li>在 &lt;code>config.yaml&lt;/code> 文件中进行 &lt;code>dubbo-proxy&lt;/code> 插件启用。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># Add this in config.yaml&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">plugins&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - ... &lt;span style="color:#586e75"># plugin you need&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - dubbo-proxy
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="3">
&lt;li>创建指向 Dubbo Provider 的 Upstream。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>curl http://127.0.0.1:9180/apisix/admin/upstreams/1 -H &lt;span style="color:#2aa198">&amp;#39;X-API-KEY: edd1c9f034335f136f87ad84b625c8f1&amp;#39;&lt;/span> -X PUT -d &lt;span style="color:#2aa198">&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">{
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;nodes&amp;#34;: {
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;127.0.0.1:20880&amp;#34;: 1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> },
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;type&amp;#34;: &amp;#34;roundrobin&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">}&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="4">
&lt;li>为 DemoService 暴露一个 HTTP 路由。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>curl http://127.0.0.1:9180/apisix/admin/routes/1 -H &lt;span style="color:#2aa198">&amp;#39;X-API-KEY: edd1c9f034335f136f87ad84b625c8f1&amp;#39;&lt;/span> -X PUT -d &lt;span style="color:#2aa198">&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">{
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;host&amp;#34;: &amp;#34;example.org&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;uris&amp;#34;: [
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;/demo&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> ],
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;plugins&amp;#34;: {
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;dubbo-proxy&amp;#34;: {
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;service_name&amp;#34;: &amp;#34;org.apache.dubbo.samples.apisix.DemoService&amp;#34;,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;service_version&amp;#34;: &amp;#34;0.0.0&amp;#34;,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;method&amp;#34;: &amp;#34;apisixDubbo&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> }
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> },
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;upstream_id&amp;#34;: 1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">}&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="5">
&lt;li>使用 curl 命令请求 Apache APISIX,并查看返回结果。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>curl http://127.0.0.1:9080/demo -H &lt;span style="color:#2aa198">&amp;#34;Host: example.org&amp;#34;&lt;/span> -X POST --data &lt;span style="color:#2aa198">&amp;#39;{&amp;#34;name&amp;#34;: &amp;#34;hello&amp;#34;}&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt; HTTP/1.1 &lt;span style="color:#2aa198">200&lt;/span> OK
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt; Date: Sun, &lt;span style="color:#2aa198">26&lt;/span> Dec &lt;span style="color:#2aa198">2021&lt;/span> 11:33:27 GMT
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt; Content-Type: text/plain; &lt;span style="color:#268bd2">charset&lt;/span>&lt;span style="color:#719e07">=&lt;/span>utf-8
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt; Content-Length: &lt;span style="color:#2aa198">14&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt; Connection: keep-alive
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt; test: &lt;span style="color:#2aa198">123&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt; Server: APISIX/2.11.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo success
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>:::note 说明
上述代码返回中包含了 &lt;code>test: 123&lt;/code> Header,以及 &lt;code>dubbo success&lt;/code> 字符串作为 Body 体。这与我们在 &lt;code>DemoServiceImpl&lt;/code> 编码的预期效果一致。
:::&lt;/p>
&lt;ol start="6">
&lt;li>查看 Dubbo Provider 的日志。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>Key = content-length, Value = 17
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Key = host, Value = example.org
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Key = content-type, Value = application/x-www-form-urlencoded
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Key = body, Value = [B@70754265
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Key = accept, Value = */*
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Key = user-agent, Value = curl/7.80.0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>:::note 说明
通过 &lt;code>httpRequestContext&lt;/code> 可以拿到 HTTP 请求的 Header 和 Body。其中 Header 会作为 Map 元素,而 Body 中 Key 值是固定的字符串&amp;quot;body&amp;quot;,Value 则代表 Byte 数组。
:::&lt;/p>
&lt;h3 id="进阶篇复杂场景示例">进阶篇:复杂场景示例&lt;/h3>
&lt;p>在上述的简单用例中可以看出,我们确实通过 Apache APISIX 将 Dubbo Service 发布为一个 HTTP 服务,但是在使用过程中的限制也非常明显。比如:接口的参数和返回值都必须要是 &lt;code>Map&amp;lt;String, Object&amp;gt;&lt;/code>。&lt;/p>
&lt;p>那么,如果项目中出现已经定义好、但又不符合上述限制的接口,该如何通过 Apache APISIX 来暴露 HTTP 服务呢?&lt;/p>
&lt;h4 id="操作步骤-1">操作步骤&lt;/h4>
&lt;p>针对上述场景,我们可以通过 HTTP Request Body 描述要调用的 Service 和 Method 以及对应参数,再利用 Java 的反射机制实现目标方法的调用。最后将返回值序列化为 JSON,并写入到 HTTP Response Body 中。&lt;/p>
&lt;p>这样就可以将 Apache APISIX 的 「HTTP to Dubbo」 能力进一步加强,并应用到所有已存在的 Dubbo Service 中。具体操作可参考下方:&lt;/p>
&lt;ol>
&lt;li>为已有项目增加一个 Dubbo Service 用来统一处理 HTTP to Dubbo 的转化。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DubboInvocationParameter&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> String type&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> String value&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DubboInvocation&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> String service&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> String method&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> DubboInvocationParameter&lt;span style="color:#719e07">[]&lt;/span> parameters&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">HTTP2DubboService&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">invoke&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> context&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> Exception&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Component&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">HTTP2DubboServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> HTTP2DubboService &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Autowired&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> ApplicationContext appContext&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">invoke&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> context&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> Exception &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> DubboInvocation invocation &lt;span style="color:#719e07">=&lt;/span> JSONObject&lt;span style="color:#719e07">.&lt;/span>parseObject&lt;span style="color:#719e07">((&lt;/span>&lt;span style="color:#dc322f">byte&lt;/span>&lt;span style="color:#719e07">[])&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;body&amp;#34;&lt;/span>&lt;span style="color:#719e07">),&lt;/span> DubboInvocation&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Object&lt;span style="color:#719e07">[]&lt;/span> args &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Object&lt;span style="color:#719e07">[&lt;/span>invocation&lt;span style="color:#719e07">.&lt;/span>getParameters&lt;span style="color:#719e07">().&lt;/span>size&lt;span style="color:#719e07">()];&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#dc322f">int&lt;/span> i &lt;span style="color:#719e07">=&lt;/span> 0&lt;span style="color:#719e07">;&lt;/span> i &lt;span style="color:#719e07">&amp;lt;&lt;/span> args&lt;span style="color:#719e07">.&lt;/span>length&lt;span style="color:#719e07">;&lt;/span> i&lt;span style="color:#719e07">++)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> DubboInvocationParameter parameter &lt;span style="color:#719e07">=&lt;/span> invocation&lt;span style="color:#719e07">.&lt;/span>getParameters&lt;span style="color:#719e07">().&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>i&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> args&lt;span style="color:#719e07">[&lt;/span>i&lt;span style="color:#719e07">]&lt;/span> &lt;span style="color:#719e07">=&lt;/span> JSONObject&lt;span style="color:#719e07">.&lt;/span>parseObject&lt;span style="color:#719e07">(&lt;/span>parameter&lt;span style="color:#719e07">.&lt;/span>getValue&lt;span style="color:#719e07">(),&lt;/span> Class&lt;span style="color:#719e07">.&lt;/span>forName&lt;span style="color:#719e07">(&lt;/span>parameter&lt;span style="color:#719e07">.&lt;/span>getType&lt;span style="color:#719e07">()));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Object svc &lt;span style="color:#719e07">=&lt;/span> appContext&lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>Class&lt;span style="color:#719e07">.&lt;/span>forName&lt;span style="color:#719e07">(&lt;/span>invocation&lt;span style="color:#719e07">.&lt;/span>getService&lt;span style="color:#719e07">()));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Object result &lt;span style="color:#719e07">=&lt;/span> svc&lt;span style="color:#719e07">.&lt;/span>getClass&lt;span style="color:#719e07">().&lt;/span>getMethod&lt;span style="color:#719e07">(&lt;/span>invocation&lt;span style="color:#719e07">.&lt;/span>getMethod&lt;span style="color:#719e07">()).&lt;/span>invoke&lt;span style="color:#719e07">(&lt;/span>args&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> httpResponse &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> HashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> httpResponse&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;status&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> 200&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> httpResponse&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;body&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> JSONObject&lt;span style="color:#719e07">.&lt;/span>toJSONString&lt;span style="color:#719e07">(&lt;/span>result&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> httpResponse&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>通过如下命令请求来发起相关调用。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>curl http://127.0.0.1:9080/demo -H &lt;span style="color:#2aa198">&amp;#34;Host: example.org&amp;#34;&lt;/span> -X POST --data &lt;span style="color:#2aa198">&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">{
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;service&amp;#34;: &amp;#34;org.apache.dubbo.samples.apisix.DemoService&amp;#34;,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;method&amp;#34;: &amp;#34;createUser&amp;#34;,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;parameters&amp;#34;: [
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> {
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;type&amp;#34;: &amp;#34;org.apache.dubbo.samples.apisix.User&amp;#34;,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;value&amp;#34;: &amp;#34;{&amp;#39;&lt;/span>name&lt;span style="color:#2aa198">&amp;#39;: &amp;#39;&lt;/span>hello&lt;span style="color:#2aa198">&amp;#39;}&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> }
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> ]
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">}&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="总结">总结&lt;/h2>
&lt;p>本文为大家介绍了如何借助 Apache APISIX 实现 Dubbo Service 的代理,通过引入 &lt;code>dubbo-proxy&lt;/code> 插件便可为 Dubbo 框架的后端系统构建更简单更高效的流量链路。&lt;/p>
&lt;p>希望通过上述操作步骤和用例场景分享,能为大家在相关场景的使用提供借鉴思路。更多关于 &lt;code>dubbo-proxy&lt;/code> 插件的介绍与使用可参考&lt;a href="https://apisix.apache.org/docs/apisix/plugins/dubbo-proxy/">官方文档&lt;/a>。&lt;/p></description></item><item><title>Blog: 分布式事务框架 seata-golang 通信模型详解</title><link>https://dubbo.apache.org/zh-cn/blog/2021/01/15/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A1%86%E6%9E%B6-seata-golang-%E9%80%9A%E4%BF%A1%E6%A8%A1%E5%9E%8B%E8%AF%A6%E8%A7%A3/</link><pubDate>Fri, 15 Jan 2021 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2021/01/15/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A1%86%E6%9E%B6-seata-golang-%E9%80%9A%E4%BF%A1%E6%A8%A1%E5%9E%8B%E8%AF%A6%E8%A7%A3/</guid><description>
&lt;h2 id="简介">简介&lt;/h2>
&lt;p>Java 的世界里,大家广泛使用一个高性能网络通信框架 —— netty,很多 RPC 框架都是基于 netty 来实现的。在 golang 的世界里,getty 也是一个类似 netty 的高性能网络通信库。getty 最初由 dubbo-go 项目负责人于雨开发,作为底层通信库在 dubbo-go 中使用。随着 dubbo-go 捐献给 apache 基金会,在社区小伙伴的共同努力下,getty 也最终进入到 apache 这个大家庭,并改名 dubbo-getty。&lt;/p>
&lt;p>18 年的时候,我在公司里实践微服务,当时遇到最大的问题就是分布式事务问题。同年,阿里在社区开源他们的分布式事务解决方案,我也很快关注到这个项目,起初还叫 fescar,后来更名 seata。由于我对开源技术很感兴趣,加了很多社区群,当时也很关注 dubbo-go 这个项目,在里面默默潜水。随着对 seata 的了解,逐渐萌生了做一个 go 版本的分布式事务框架的想法。&lt;/p>
&lt;p>要做一个 golang 版的分布式事务框架,首先需要解决的一个问题就是如何实现 RPC 通信。dubbo-go 就是摆在眼前很好的一个例子,遂开始研究 dubbo-go 的底层 getty。&lt;/p>
&lt;h2 id="如何基于-getty-实现-rpc-通信">如何基于 getty 实现 RPC 通信&lt;/h2>
&lt;p>getty 框架的整体模型图如下:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/dubbo-go/seata/p1.webp" alt="img">&lt;/p>
&lt;p>下面结合相关代码,详述 seata-golang 的 RPC 通信过程。&lt;/p>
&lt;h3 id="1-建立连接">1. 建立连接&lt;/h3>
&lt;p>实现 RPC 通信,首先要建立网络连接,这里先从 client.go 开始看起。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (c &lt;span style="color:#719e07">*&lt;/span>client) &lt;span style="color:#268bd2">connect&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">var&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> err &lt;span style="color:#dc322f">error&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ss Session
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> )
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 建立一个 session 连接
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ss = c.&lt;span style="color:#268bd2">dial&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> ss &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// client has been closed
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">break&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> err = c.&lt;span style="color:#268bd2">newSession&lt;/span>(ss)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 收发报文
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ss.(&lt;span style="color:#719e07">*&lt;/span>session).&lt;span style="color:#268bd2">run&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 此处省略部分代码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">break&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// don&amp;#39;t distinguish between tcp connection and websocket connection. Because
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// gorilla/websocket/conn.go:(Conn)Close also invoke net.Conn.Close()
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ss.&lt;span style="color:#268bd2">Conn&lt;/span>().&lt;span style="color:#268bd2">Close&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>connect()&lt;/code> 方法通过 &lt;code>dial()&lt;/code> 方法得到了一个 session 连接,进入 &lt;code>dial()&lt;/code> 方法:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (c &lt;span style="color:#719e07">*&lt;/span>client) &lt;span style="color:#268bd2">dial&lt;/span>() Session {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">switch&lt;/span> c.endPointType {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> TCP_CLIENT:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> c.&lt;span style="color:#268bd2">dialTCP&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> UDP_CLIENT:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> c.&lt;span style="color:#268bd2">dialUDP&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> WS_CLIENT:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> c.&lt;span style="color:#268bd2">dialWS&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> WSS_CLIENT:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> c.&lt;span style="color:#268bd2">dialWSS&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>我们关注的是 TCP 连接,所以继续进入 &lt;code>c.dialTCP()&lt;/code> 方法:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (c &lt;span style="color:#719e07">*&lt;/span>client) &lt;span style="color:#268bd2">dialTCP&lt;/span>() Session {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">var&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> err &lt;span style="color:#dc322f">error&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> conn net.Conn
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> )
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> c.&lt;span style="color:#268bd2">IsClosed&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> c.sslEnabled {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> sslConfig, err &lt;span style="color:#719e07">:=&lt;/span> c.tlsConfigBuilder.&lt;span style="color:#268bd2">BuildTlsConfig&lt;/span>(); err &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> sslConfig &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> d &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#719e07">&amp;amp;&lt;/span>net.Dialer{Timeout: connectTimeout}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 建立加密连接
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> conn, err = tls.&lt;span style="color:#268bd2">DialWithDialer&lt;/span>(d, &lt;span style="color:#2aa198">&amp;#34;tcp&amp;#34;&lt;/span>, c.addr, sslConfig)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 建立 tcp 连接
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> conn, err = net.&lt;span style="color:#268bd2">DialTimeout&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;tcp&amp;#34;&lt;/span>, c.addr, connectTimeout)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> gxnet.&lt;span style="color:#268bd2">IsSameAddr&lt;/span>(conn.&lt;span style="color:#268bd2">RemoteAddr&lt;/span>(), conn.&lt;span style="color:#268bd2">LocalAddr&lt;/span>()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> conn.&lt;span style="color:#268bd2">Close&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> err = errSelfConnect
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 返回一个 TCPSession
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#268bd2">newTCPSession&lt;/span>(conn, c)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#268bd2">Infof&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;net.DialTimeout(addr:%s, timeout:%v) = error:%+v&amp;#34;&lt;/span>, c.addr, connectTimeout, perrors.&lt;span style="color:#268bd2">WithStack&lt;/span>(err))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;-&lt;/span>wheel.&lt;span style="color:#268bd2">After&lt;/span>(connectInterval)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>至此,我们知道了 getty 如何建立 TCP 连接,并返回 TCPSession。&lt;/p>
&lt;h3 id="2-收发报文">2. 收发报文&lt;/h3>
&lt;p>那它是怎么收发报文的呢,我们回到 connection 方法接着往下看,有这样一行 &lt;code>ss.(*session).run()&lt;/code>,在这行代码之后,代码都是很简单的操作,我们猜测这行代码运行的逻辑里面一定包含收发报文的逻辑,接着进入 &lt;code>run()&lt;/code> 方法:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (s &lt;span style="color:#719e07">*&lt;/span>session) &lt;span style="color:#268bd2">run&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 省略部分代码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">go&lt;/span> s.&lt;span style="color:#268bd2">handleLoop&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">go&lt;/span> s.&lt;span style="color:#268bd2">handlePackage&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里起了两个 goroutine:&lt;code>handleLoop&lt;/code> 和 &lt;code>handlePackage&lt;/code>,看字面意思符合我们的猜想,进入 &lt;code>handleLoop()&lt;/code> 方法:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (s &lt;span style="color:#719e07">*&lt;/span>session) &lt;span style="color:#268bd2">handleLoop&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 省略部分代码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// A select blocks until one of its cases is ready to run.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// It choose one at random if multiple are ready. Otherwise it choose default branch if none is ready.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">select&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 省略部分代码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> outPkg, ok = &lt;span style="color:#719e07">&amp;lt;-&lt;/span>s.wQ:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 省略部分代码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> iovec = iovec[:&lt;span style="color:#2aa198">0&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> idx &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>; idx &amp;lt; maxIovecNum; idx&lt;span style="color:#719e07">++&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 通过 s.writer 将 interface{} 类型的 outPkg 编码成二进制的比特
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> pkgBytes, err = s.writer.&lt;span style="color:#268bd2">Write&lt;/span>(s, outPkg)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 省略部分代码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> iovec = &lt;span style="color:#b58900">append&lt;/span>(iovec, pkgBytes)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//省略部分代码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 将这些二进制比特发送出去
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> err = s.&lt;span style="color:#268bd2">WriteBytesArray&lt;/span>(iovec[:]&lt;span style="color:#719e07">...&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#268bd2">Errorf&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;%s, [session.handleLoop]s.WriteBytesArray(iovec len:%d) = error:%+v&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> s.&lt;span style="color:#268bd2">sessionToken&lt;/span>(), &lt;span style="color:#b58900">len&lt;/span>(iovec), perrors.&lt;span style="color:#268bd2">WithStack&lt;/span>(err))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> s.&lt;span style="color:#268bd2">stop&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// break LOOP
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> flag = &lt;span style="color:#cb4b16">false&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> &lt;span style="color:#719e07">&amp;lt;-&lt;/span>wheel.&lt;span style="color:#268bd2">After&lt;/span>(s.period):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> flag {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> wsFlag {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> err &lt;span style="color:#719e07">:=&lt;/span> wsConn.&lt;span style="color:#268bd2">writePing&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#268bd2">Warnf&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;wsConn.writePing() = error:%+v&amp;#34;&lt;/span>, perrors.&lt;span style="color:#268bd2">WithStack&lt;/span>(err))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 定时执行的逻辑,心跳等
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> s.listener.&lt;span style="color:#268bd2">OnCron&lt;/span>(s)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>通过上面的代码,我们不难发现,&lt;code>handleLoop()&lt;/code> 方法处理的是发送报文的逻辑,RPC 需要发送的消息首先由 &lt;code>s.writer&lt;/code> 编码成二进制比特,然后通过建立的 TCP 连接发送出去。这个 &lt;code>s.writer&lt;/code> 对应的 Writer 接口是 RPC 框架必须要实现的一个接口。&lt;/p>
&lt;p>继续看 &lt;code>handlePackage()&lt;/code> 方法:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (s &lt;span style="color:#719e07">*&lt;/span>session) &lt;span style="color:#268bd2">handlePackage&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 省略部分代码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> _, ok &lt;span style="color:#719e07">:=&lt;/span> s.Connection.(&lt;span style="color:#719e07">*&lt;/span>gettyTCPConn); ok {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> s.reader &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> errStr &lt;span style="color:#719e07">:=&lt;/span> fmt.&lt;span style="color:#268bd2">Sprintf&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;session{name:%s, conn:%#v, reader:%#v}&amp;#34;&lt;/span>, s.name, s.Connection, s.reader)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#268bd2">Error&lt;/span>(errStr)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b58900">panic&lt;/span>(errStr)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> err = s.&lt;span style="color:#268bd2">handleTCPPackage&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">if&lt;/span> _, ok &lt;span style="color:#719e07">:=&lt;/span> s.Connection.(&lt;span style="color:#719e07">*&lt;/span>gettyWSConn); ok {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> err = s.&lt;span style="color:#268bd2">handleWSPackage&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">if&lt;/span> _, ok &lt;span style="color:#719e07">:=&lt;/span> s.Connection.(&lt;span style="color:#719e07">*&lt;/span>gettyUDPConn); ok {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> err = s.&lt;span style="color:#268bd2">handleUDPPackage&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b58900">panic&lt;/span>(fmt.&lt;span style="color:#268bd2">Sprintf&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;unknown type session{%#v}&amp;#34;&lt;/span>, s))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>进入 &lt;code>handleTCPPackage()&lt;/code> 方法:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (s &lt;span style="color:#719e07">*&lt;/span>session) &lt;span style="color:#268bd2">handleTCPPackage&lt;/span>() &lt;span style="color:#dc322f">error&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 省略部分代码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> conn = s.Connection.(&lt;span style="color:#719e07">*&lt;/span>gettyTCPConn)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 省略部分代码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bufLen = &lt;span style="color:#2aa198">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// for clause for the network timeout condition check
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// s.conn.SetReadTimeout(time.Now().Add(s.rTimeout))
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// 从 TCP 连接中收到报文
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> bufLen, err = conn.&lt;span style="color:#268bd2">recv&lt;/span>(buf)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 省略部分代码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">break&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 省略部分代码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 将收到的报文二进制比特写入 pkgBuf
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> pktBuf.&lt;span style="color:#268bd2">Write&lt;/span>(buf[:bufLen])
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> pktBuf.&lt;span style="color:#268bd2">Len&lt;/span>() &lt;span style="color:#719e07">&amp;lt;=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">break&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 通过 s.reader 将收到的报文解码成 RPC 消息
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> pkg, pkgLen, err = s.reader.&lt;span style="color:#268bd2">Read&lt;/span>(s, pktBuf.&lt;span style="color:#268bd2">Bytes&lt;/span>())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 省略部分代码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> s.&lt;span style="color:#268bd2">UpdateActive&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 将收到的消息放入 TaskQueue 供 RPC 消费端消费
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> s.&lt;span style="color:#268bd2">addTask&lt;/span>(pkg)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> pktBuf.&lt;span style="color:#268bd2">Next&lt;/span>(pkgLen)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// continue to handle case 5
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> exit {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">break&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> perrors.&lt;span style="color:#268bd2">WithStack&lt;/span>(err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>从上面的代码逻辑我们分析出,RPC 消费端需要将从 TCP 连接收到的二进制比特报文解码成 RPC 能消费的消息,这个工作由 s.reader 实现,所以,我们要构建 RPC 通信层也需要实现 s.reader 对应的 Reader 接口。&lt;/p>
&lt;h3 id="3-底层处理网络报文的逻辑如何与业务逻辑解耦">3. 底层处理网络报文的逻辑如何与业务逻辑解耦&lt;/h3>
&lt;p>我们都知道,netty 通过 boss 线程和 worker 线程实现了底层网络逻辑和业务逻辑的解耦。那么,getty 是如何实现的呢?&lt;/p>
&lt;p>在 &lt;code>handlePackage()&lt;/code> 方法最后,我们看到,收到的消息被放入了 &lt;code>s.addTask(pkg)&lt;/code> 这个方法,接着往下分析:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (s &lt;span style="color:#719e07">*&lt;/span>session) &lt;span style="color:#268bd2">addTask&lt;/span>(pkg &lt;span style="color:#268bd2">interface&lt;/span>{}) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> f &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#268bd2">func&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> s.listener.&lt;span style="color:#268bd2">OnMessage&lt;/span>(s, pkg)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> s.&lt;span style="color:#268bd2">incReadPkgNum&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> taskPool &lt;span style="color:#719e07">:=&lt;/span> s.&lt;span style="color:#268bd2">EndPoint&lt;/span>().&lt;span style="color:#268bd2">GetTaskPool&lt;/span>(); taskPool &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> taskPool.&lt;span style="color:#268bd2">AddTaskAlways&lt;/span>(f)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">f&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>pkg&lt;/code> 参数传递到了一个匿名方法,这个方法最终放入了 &lt;code>taskPool&lt;/code>。这个方法很关键,在我后来写 seata-golang 代码的时候,就遇到了一个坑,这个坑后面分析。&lt;/p>
&lt;p>接着我们看一下 taskPool 的定义:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// NewTaskPoolSimple build a simple task pool
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">NewTaskPoolSimple&lt;/span>(size &lt;span style="color:#dc322f">int&lt;/span>) GenericTaskPool {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> size &amp;lt; &lt;span style="color:#2aa198">1&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> size = runtime.&lt;span style="color:#268bd2">NumCPU&lt;/span>() &lt;span style="color:#719e07">*&lt;/span> &lt;span style="color:#2aa198">100&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">&amp;amp;&lt;/span>taskPoolSimple{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> work: &lt;span style="color:#b58900">make&lt;/span>(&lt;span style="color:#268bd2">chan&lt;/span> task),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> sem: &lt;span style="color:#b58900">make&lt;/span>(&lt;span style="color:#268bd2">chan&lt;/span> &lt;span style="color:#268bd2">struct&lt;/span>{}, size),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> done: &lt;span style="color:#b58900">make&lt;/span>(&lt;span style="color:#268bd2">chan&lt;/span> &lt;span style="color:#268bd2">struct&lt;/span>{}),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>构建了一个缓冲大小为 size (默认为 &lt;code>runtime.NumCPU() * 100&lt;/code>) 的 channel &lt;code>sem&lt;/code>。再看方法 &lt;code>AddTaskAlways(t task)&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (p &lt;span style="color:#719e07">*&lt;/span>taskPoolSimple) &lt;span style="color:#268bd2">AddTaskAlways&lt;/span>(t task) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">select&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> &lt;span style="color:#719e07">&amp;lt;-&lt;/span>p.done:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">default&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">select&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> p.work &lt;span style="color:#719e07">&amp;lt;-&lt;/span> t:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">default&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">select&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> p.work &lt;span style="color:#719e07">&amp;lt;-&lt;/span> t:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> p.sem &lt;span style="color:#719e07">&amp;lt;-&lt;/span> &lt;span style="color:#268bd2">struct&lt;/span>{}{}:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> p.wg.&lt;span style="color:#268bd2">Add&lt;/span>(&lt;span style="color:#2aa198">1&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">go&lt;/span> p.&lt;span style="color:#268bd2">worker&lt;/span>(t)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">default&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">goSafely&lt;/span>(t)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>加入的任务,会先由 len(p.sem) 个 goroutine 去消费,如果没有 goroutine 空闲,则会启动一个临时的 goroutine 去运行 t()。相当于有 len(p.sem) 个 goroutine 组成了 goroutine pool,pool 中的 goroutine 去处理业务逻辑,而不是由处理网络报文的 goroutine 去运行业务逻辑,从而实现了解耦。写 seata-golang 时遇到的一个坑,就是忘记设置 taskPool 造成了处理业务逻辑和处理底层网络报文逻辑的 goroutine 是同一个,我在业务逻辑中阻塞等待一个任务完成时,阻塞了整个 goroutine,使得阻塞期间收不到任何报文。&lt;/p>
&lt;h3 id="4-具体实现">4. 具体实现&lt;/h3>
&lt;p>下面的代码见getty.go :&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// Reader is used to unmarshal a complete pkg from buffer
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">type&lt;/span> Reader &lt;span style="color:#268bd2">interface&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">Read&lt;/span>(Session, []&lt;span style="color:#dc322f">byte&lt;/span>) (&lt;span style="color:#268bd2">interface&lt;/span>{}, &lt;span style="color:#dc322f">int&lt;/span>, &lt;span style="color:#dc322f">error&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// Writer is used to marshal pkg and write to session
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">type&lt;/span> Writer &lt;span style="color:#268bd2">interface&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// if @Session is udpGettySession, the second parameter is UDPContext.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">Write&lt;/span>(Session, &lt;span style="color:#268bd2">interface&lt;/span>{}) ([]&lt;span style="color:#dc322f">byte&lt;/span>, &lt;span style="color:#dc322f">error&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// ReadWriter interface use for handle application packages
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">type&lt;/span> ReadWriter &lt;span style="color:#268bd2">interface&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Reader
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Writer
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// EventListener is used to process pkg that received from remote session
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">type&lt;/span> EventListener &lt;span style="color:#268bd2">interface&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// invoked when session opened
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// If the return error is not nil, @Session will be closed.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">OnOpen&lt;/span>(Session) &lt;span style="color:#dc322f">error&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// invoked when session closed.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">OnClose&lt;/span>(Session)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// invoked when got error.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">OnError&lt;/span>(Session, &lt;span style="color:#dc322f">error&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// invoked periodically, its period can be set by (Session)SetCronPeriod
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">OnCron&lt;/span>(Session)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// invoked when getty received a package. Pls attention that do not handle long time
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// logic processing in this func. You&amp;#39;d better set the package&amp;#39;s maximum length.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// If the message&amp;#39;s length is greater than it, u should should return err in
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// Reader{Read} and getty will close this connection soon.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// If ur logic processing in this func will take a long time, u should start a goroutine
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// pool(like working thread pool in cpp) to handle the processing asynchronously. Or u
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// can do the logic processing in other asynchronous way.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// !!!In short, ur OnMessage callback func should return asap.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// If this is a udp event listener, the second parameter type is UDPContext.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">OnMessage&lt;/span>(Session, &lt;span style="color:#268bd2">interface&lt;/span>{})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>通过对整个 getty 代码的分析,我们只要实现 &lt;code>ReadWriter&lt;/code> 来对 RPC 消息编解码,再实现 &lt;code>EventListener&lt;/code> 来处理 RPC 消息的对应的具体逻辑,将 &lt;code>ReadWriter&lt;/code> 实现和 &lt;code>EventLister&lt;/code> 实现注入到 RPC 的 Client 和 Server 端,则可实现 RPC 通信。&lt;/p>
&lt;h3 id="1编解码协议实现">1)编解码协议实现&lt;/h3>
&lt;p>下面是 seata 协议的定义:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/dubbo-go/seata/p2.webp" alt="img">&lt;/p>
&lt;p>在 ReadWriter 接口的实现 &lt;code>RpcPackageHandler&lt;/code> 中,调用 Codec 方法对消息体按照上面的格式编解码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>// 消息编码为二进制比特
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>func MessageEncoder(codecType byte, in interface{}) []byte {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> switch codecType {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> case SEATA:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> return SeataEncoder(in)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> default:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.Errorf(&amp;#34;not support codecType, %s&amp;#34;, codecType)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> return nil
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>// 二进制比特解码为消息体
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>func MessageDecoder(codecType byte, in []byte) (interface{}, int) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> switch codecType {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> case SEATA:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> return SeataDecoder(in)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> default:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.Errorf(&amp;#34;not support codecType, %s&amp;#34;, codecType)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> return nil, 0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="2client-端实现">2)Client 端实现&lt;/h3>
&lt;p>再来看 client 端 &lt;code>EventListener&lt;/code> 的实现 &lt;code>RpcRemotingClient&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (client &lt;span style="color:#719e07">*&lt;/span>RpcRemoteClient) &lt;span style="color:#268bd2">OnOpen&lt;/span>(session getty.Session) &lt;span style="color:#dc322f">error&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">go&lt;/span> &lt;span style="color:#268bd2">func&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> request &lt;span style="color:#719e07">:=&lt;/span> protocal.RegisterTMRequest{AbstractIdentifyRequest: protocal.AbstractIdentifyRequest{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ApplicationId: client.conf.ApplicationId,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> TransactionServiceGroup: client.conf.TransactionServiceGroup,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 建立连接后向 Transaction Coordinator 发起注册 TransactionManager 的请求
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> _, err &lt;span style="color:#719e07">:=&lt;/span> client.&lt;span style="color:#268bd2">sendAsyncRequestWithResponse&lt;/span>(session, request, RPC_REQUEST_TIMEOUT)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 将与 Transaction Coordinator 建立的连接保存在连接池供后续使用
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> clientSessionManager.&lt;span style="color:#268bd2">RegisterGettySession&lt;/span>(session)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> client.GettySessionOnOpenChannel &lt;span style="color:#719e07">&amp;lt;-&lt;/span> session.&lt;span style="color:#268bd2">RemoteAddr&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// OnError ...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">func&lt;/span> (client &lt;span style="color:#719e07">*&lt;/span>RpcRemoteClient) &lt;span style="color:#268bd2">OnError&lt;/span>(session getty.Session, err &lt;span style="color:#dc322f">error&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> clientSessionManager.&lt;span style="color:#268bd2">ReleaseGettySession&lt;/span>(session)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// OnClose ...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">func&lt;/span> (client &lt;span style="color:#719e07">*&lt;/span>RpcRemoteClient) &lt;span style="color:#268bd2">OnClose&lt;/span>(session getty.Session) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> clientSessionManager.&lt;span style="color:#268bd2">ReleaseGettySession&lt;/span>(session)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// OnMessage ...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">func&lt;/span> (client &lt;span style="color:#719e07">*&lt;/span>RpcRemoteClient) &lt;span style="color:#268bd2">OnMessage&lt;/span>(session getty.Session, pkg &lt;span style="color:#268bd2">interface&lt;/span>{}) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#268bd2">Info&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;received message:{%v}&amp;#34;&lt;/span>, pkg)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> rpcMessage, ok &lt;span style="color:#719e07">:=&lt;/span> pkg.(protocal.RpcMessage)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> ok {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> heartBeat, isHeartBeat &lt;span style="color:#719e07">:=&lt;/span> rpcMessage.Body.(protocal.HeartBeatMessage)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> isHeartBeat &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> heartBeat &lt;span style="color:#719e07">==&lt;/span> protocal.HeartBeatMessagePong {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#268bd2">Debugf&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;received PONG from %s&amp;#34;&lt;/span>, session.&lt;span style="color:#268bd2">RemoteAddr&lt;/span>())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> rpcMessage.MessageType &lt;span style="color:#719e07">==&lt;/span> protocal.MSGTYPE_RESQUEST &lt;span style="color:#719e07">||&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> rpcMessage.MessageType &lt;span style="color:#719e07">==&lt;/span> protocal.MSGTYPE_RESQUEST_ONEWAY {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#268bd2">Debugf&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;msgId:%s, body:%v&amp;#34;&lt;/span>, rpcMessage.Id, rpcMessage.Body)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 处理事务消息,提交 or 回滚
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> client.&lt;span style="color:#268bd2">onMessage&lt;/span>(rpcMessage, session.&lt;span style="color:#268bd2">RemoteAddr&lt;/span>())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> resp, loaded &lt;span style="color:#719e07">:=&lt;/span> client.futures.&lt;span style="color:#268bd2">Load&lt;/span>(rpcMessage.Id)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> loaded {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> response &lt;span style="color:#719e07">:=&lt;/span> resp.(&lt;span style="color:#719e07">*&lt;/span>getty2.MessageFuture)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> response.Response = rpcMessage.Body
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> response.Done &lt;span style="color:#719e07">&amp;lt;-&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> client.futures.&lt;span style="color:#268bd2">Delete&lt;/span>(rpcMessage.Id)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// OnCron ...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">func&lt;/span> (client &lt;span style="color:#719e07">*&lt;/span>RpcRemoteClient) &lt;span style="color:#268bd2">OnCron&lt;/span>(session getty.Session) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 发送心跳
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> client.&lt;span style="color:#268bd2">defaultSendRequest&lt;/span>(session, protocal.HeartBeatMessagePing)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>clientSessionManager.RegisterGettySession(session)&lt;/code> 的逻辑将在下文中分析。&lt;/p>
&lt;h3 id="3server-端-transaction-coordinator-实现">3)Server 端 Transaction Coordinator 实现&lt;/h3>
&lt;p>代码见 &lt;code>DefaultCoordinator&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (coordinator &lt;span style="color:#719e07">*&lt;/span>DefaultCoordinator) &lt;span style="color:#268bd2">OnOpen&lt;/span>(session getty.Session) &lt;span style="color:#dc322f">error&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#268bd2">Infof&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;got getty_session:%s&amp;#34;&lt;/span>, session.&lt;span style="color:#268bd2">Stat&lt;/span>())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (coordinator &lt;span style="color:#719e07">*&lt;/span>DefaultCoordinator) &lt;span style="color:#268bd2">OnError&lt;/span>(session getty.Session, err &lt;span style="color:#dc322f">error&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 释放 TCP 连接
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> SessionManager.&lt;span style="color:#268bd2">ReleaseGettySession&lt;/span>(session)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> session.&lt;span style="color:#268bd2">Close&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#268bd2">Errorf&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;getty_session{%s} got error{%v}, will be closed.&amp;#34;&lt;/span>, session.&lt;span style="color:#268bd2">Stat&lt;/span>(), err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (coordinator &lt;span style="color:#719e07">*&lt;/span>DefaultCoordinator) &lt;span style="color:#268bd2">OnClose&lt;/span>(session getty.Session) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#268bd2">Info&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;getty_session{%s} is closing......&amp;#34;&lt;/span>, session.&lt;span style="color:#268bd2">Stat&lt;/span>())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (coordinator &lt;span style="color:#719e07">*&lt;/span>DefaultCoordinator) &lt;span style="color:#268bd2">OnMessage&lt;/span>(session getty.Session, pkg &lt;span style="color:#268bd2">interface&lt;/span>{}) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#268bd2">Debugf&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;received message:{%v}&amp;#34;&lt;/span>, pkg)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> rpcMessage, ok &lt;span style="color:#719e07">:=&lt;/span> pkg.(protocal.RpcMessage)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> ok {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> _, isRegTM &lt;span style="color:#719e07">:=&lt;/span> rpcMessage.Body.(protocal.RegisterTMRequest)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> isRegTM {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 将 TransactionManager 信息和 TCP 连接建立映射关系
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> coordinator.&lt;span style="color:#268bd2">OnRegTmMessage&lt;/span>(rpcMessage, session)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> heartBeat, isHeartBeat &lt;span style="color:#719e07">:=&lt;/span> rpcMessage.Body.(protocal.HeartBeatMessage)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> isHeartBeat &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> heartBeat &lt;span style="color:#719e07">==&lt;/span> protocal.HeartBeatMessagePing {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> coordinator.&lt;span style="color:#268bd2">OnCheckMessage&lt;/span>(rpcMessage, session)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> rpcMessage.MessageType &lt;span style="color:#719e07">==&lt;/span> protocal.MSGTYPE_RESQUEST &lt;span style="color:#719e07">||&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> rpcMessage.MessageType &lt;span style="color:#719e07">==&lt;/span> protocal.MSGTYPE_RESQUEST_ONEWAY {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#268bd2">Debugf&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;msgId:%s, body:%v&amp;#34;&lt;/span>, rpcMessage.Id, rpcMessage.Body)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> _, isRegRM &lt;span style="color:#719e07">:=&lt;/span> rpcMessage.Body.(protocal.RegisterRMRequest)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> isRegRM {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 将 ResourceManager 信息和 TCP 连接建立映射关系
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> coordinator.&lt;span style="color:#268bd2">OnRegRmMessage&lt;/span>(rpcMessage, session)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> SessionManager.&lt;span style="color:#268bd2">IsRegistered&lt;/span>(session) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">defer&lt;/span> &lt;span style="color:#268bd2">func&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#b58900">recover&lt;/span>(); err &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#268bd2">Errorf&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;Catch Exception while do RPC, request: %v,err: %w&amp;#34;&lt;/span>, rpcMessage, err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 处理事务消息,全局事务注册、分支事务注册、分支事务提交、全局事务回滚等
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> coordinator.&lt;span style="color:#268bd2">OnTrxMessage&lt;/span>(rpcMessage, session)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> session.&lt;span style="color:#268bd2">Close&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#268bd2">Infof&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;close a unhandled connection! [%v]&amp;#34;&lt;/span>, session)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> resp, loaded &lt;span style="color:#719e07">:=&lt;/span> coordinator.futures.&lt;span style="color:#268bd2">Load&lt;/span>(rpcMessage.Id)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> loaded {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> response &lt;span style="color:#719e07">:=&lt;/span> resp.(&lt;span style="color:#719e07">*&lt;/span>getty2.MessageFuture)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> response.Response = rpcMessage.Body
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> response.Done &lt;span style="color:#719e07">&amp;lt;-&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> coordinator.futures.&lt;span style="color:#268bd2">Delete&lt;/span>(rpcMessage.Id)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (coordinator &lt;span style="color:#719e07">*&lt;/span>DefaultCoordinator) &lt;span style="color:#268bd2">OnCron&lt;/span>(session getty.Session) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>coordinator.OnRegTmMessage(rpcMessage, session)&lt;/code> 注册 Transaction Manager,&lt;code>coordinator.OnRegRmMessage(rpcMessage, session)&lt;/code> 注册 Resource Manager。具体逻辑分析见下文。&lt;/p>
&lt;p>消息进入 &lt;code>coordinator.OnTrxMessage(rpcMessage, session)&lt;/code> 方法,将按照消息的类型码路由到具体的逻辑当中:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">switch&lt;/span> msg.&lt;span style="color:#268bd2">GetTypeCode&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> protocal.TypeGlobalBegin:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> req &lt;span style="color:#719e07">:=&lt;/span> msg.(protocal.GlobalBeginRequest)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> resp &lt;span style="color:#719e07">:=&lt;/span> coordinator.&lt;span style="color:#268bd2">doGlobalBegin&lt;/span>(req, ctx)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> resp
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> protocal.TypeGlobalStatus:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> req &lt;span style="color:#719e07">:=&lt;/span> msg.(protocal.GlobalStatusRequest)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> resp &lt;span style="color:#719e07">:=&lt;/span> coordinator.&lt;span style="color:#268bd2">doGlobalStatus&lt;/span>(req, ctx)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> resp
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> protocal.TypeGlobalReport:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> req &lt;span style="color:#719e07">:=&lt;/span> msg.(protocal.GlobalReportRequest)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> resp &lt;span style="color:#719e07">:=&lt;/span> coordinator.&lt;span style="color:#268bd2">doGlobalReport&lt;/span>(req, ctx)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> resp
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> protocal.TypeGlobalCommit:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> req &lt;span style="color:#719e07">:=&lt;/span> msg.(protocal.GlobalCommitRequest)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> resp &lt;span style="color:#719e07">:=&lt;/span> coordinator.&lt;span style="color:#268bd2">doGlobalCommit&lt;/span>(req, ctx)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> resp
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> protocal.TypeGlobalRollback:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> req &lt;span style="color:#719e07">:=&lt;/span> msg.(protocal.GlobalRollbackRequest)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> resp &lt;span style="color:#719e07">:=&lt;/span> coordinator.&lt;span style="color:#268bd2">doGlobalRollback&lt;/span>(req, ctx)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> resp
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> protocal.TypeBranchRegister:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> req &lt;span style="color:#719e07">:=&lt;/span> msg.(protocal.BranchRegisterRequest)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> resp &lt;span style="color:#719e07">:=&lt;/span> coordinator.&lt;span style="color:#268bd2">doBranchRegister&lt;/span>(req, ctx)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> resp
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> protocal.TypeBranchStatusReport:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> req &lt;span style="color:#719e07">:=&lt;/span> msg.(protocal.BranchReportRequest)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> resp &lt;span style="color:#719e07">:=&lt;/span> coordinator.&lt;span style="color:#268bd2">doBranchReport&lt;/span>(req, ctx)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> resp
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">default&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="4session-manager-分析">4)session manager 分析&lt;/h3>
&lt;p>Client 端同 Transaction Coordinator 建立连接起连接后,通过 &lt;code>clientSessionManager.RegisterGettySession(session)&lt;/code> 将连接保存在 &lt;code>serverSessions = sync.Map{}&lt;/code> 这个 map 中。map 的 key 为从 session 中获取的 RemoteAddress 即 Transaction Coordinator 的地址,value 为 session。这样,Client 端就可以通过 map 中的一个 session 来向 Transaction Coordinator 注册 Transaction Manager 和 Resource Manager 了。具体代码见 &lt;code>getty_client_session_manager.go&lt;/code>。&lt;/p>
&lt;p>Transaction Manager 和 Resource Manager 注册到 Transaction Coordinator 后,一个连接既有可能用来发送 TM 消息也有可能用来发送 RM 消息。我们通过 RpcContext 来标识一个连接信息:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">type&lt;/span> RpcContext &lt;span style="color:#268bd2">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Version &lt;span style="color:#dc322f">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> TransactionServiceGroup &lt;span style="color:#dc322f">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ClientRole meta.TransactionRole
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ApplicationId &lt;span style="color:#dc322f">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ClientId &lt;span style="color:#dc322f">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ResourceSets &lt;span style="color:#719e07">*&lt;/span>model.Set
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Session getty.Session
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>当收到事务消息时,我们需要构造这样一个 RpcContext 供后续事务处理逻辑使用。所以,我们会构造下列 map 来缓存映射关系:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">var&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// session -&amp;gt; transactionRole
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// TM will register before RM, if a session is not the TM registered,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// it will be the RM registered
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> session_transactionroles = sync.Map{}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// session -&amp;gt; applicationId
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> identified_sessions = sync.Map{}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// applicationId -&amp;gt; ip -&amp;gt; port -&amp;gt; session
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> client_sessions = sync.Map{}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// applicationId -&amp;gt; resourceIds
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> client_resources = sync.Map{}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这样,Transaction Manager 和 Resource Manager 分别通过 &lt;code>coordinator.OnRegTmMessage(rpcMessage, session)&lt;/code> 和 &lt;code>coordinator.OnRegRmMessage(rpcMessage, session)&lt;/code> 注册到 Transaction Coordinator 时,会在上述 client_sessions map 中缓存 applicationId、ip、port 与 session 的关系,在 client_resources map 中缓存 applicationId 与 resourceIds(一个应用可能存在多个 Resource Manager) 的关系。&lt;/p>
&lt;p>在需要时,我们就可以通过上述映射关系构造一个 RpcContext。这部分的实现和 java 版 seata 有很大的不同,感兴趣的可以深入了解一下。具体代码见 &lt;code>getty_session_manager.go&lt;/code>。&lt;/p>
&lt;p>至此,我们就分析完了 seata-golang 整个 RPC 通信模型的机制。&lt;/p>
&lt;h3 id="seata-golang-的未来">seata-golang 的未来&lt;/h3>
&lt;p>seata-golang 从今年 4 月份开始开发,到 8 月份基本实现和 java 版 seata 1.2 协议的互通,对 mysql 数据库实现了 AT 模式(自动协调分布式事务的提交回滚),实现了 TCC 模式,TC 端使用 mysql 存储数据,使 TC 变成一个无状态应用支持高可用部署。下图展示了 AT 模式的原理:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/dubbo-go/seata/p3.webp" alt="img">&lt;/p>
&lt;p>后续,还有许多工作可以做,比如:对注册中心的支持、对配置中心的支持、和 java 版 seata 1.4 的协议互通、其他数据库的支持、raft transaction coordinator 的实现等,希望对分布式事务问题感兴趣的开发者可以加入进来一起来打造一个完善的 golang 的分布式事务框架。如果你有任何疑问,欢迎钉钉扫码加入交流群【钉钉群号 33069364】:&lt;/p>
&lt;p>另外,欢迎对 dubbogo 感兴趣的朋友到 dubbogo 社区钉钉群(钉钉群号 31363295)沟通 dubbogo 技术问题。&lt;/p>
&lt;h3 id="参考资料">参考资料&lt;/h3>
&lt;ul>
&lt;li>seata 官方: &lt;a href="https://seata.io">https://seata.io&lt;/a>&lt;/li>
&lt;li>java 版 seata: &lt;a href="https://github.com/seata/seata">https://github.com/seata/seata&lt;/a>&lt;/li>
&lt;li>seata-golang 项目地址: &lt;a href="https://github.com/opentrx/seata-golang">https://github.com/opentrx/seata-golang&lt;/a>&lt;/li>
&lt;li>seata-golang go 夜读 b 站分享: &lt;a href="https://www.bilibili.com/video/BV1oz411e72T">https://www.bilibili.com/video/BV1oz411e72T&lt;/a>&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>作者简介&lt;/p>
&lt;p>&lt;strong>刘晓敏&lt;/strong> (GitHubID dk-lockdown),目前就职于 h3c 成都分公司,擅长使用 Java/Go 语言,在云原生和微服务相关技术方向均有涉猎,目前专攻分布式事务。
&lt;strong>于雨&lt;/strong>((github @AlexStocks),dubbo-go 项目和社区负责人,一个有十多年服务端基础架构研发一线工作经验的程序员,陆续参与改进过 Muduo/Pika/Dubbo/Sentinel-go 等知名项目,目前在蚂蚁金服可信原生部从事容器编排和 service mesh 工作。&lt;/p>
&lt;/blockquote></description></item><item><title>Blog: 使用Apache Skywalking (Incubator) 做分布式跟踪</title><link>https://dubbo.apache.org/zh-cn/blog/2019/08/11/%E4%BD%BF%E7%94%A8apache-skywalking-incubator-%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E8%B7%9F%E8%B8%AA/</link><pubDate>Sun, 11 Aug 2019 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2019/08/11/%E4%BD%BF%E7%94%A8apache-skywalking-incubator-%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E8%B7%9F%E8%B8%AA/</guid><description>
&lt;h2 id="apache-skywalkingincubator简介">Apache Skywalking(Incubator)简介&lt;/h2>
&lt;p>&lt;a href="https://github.com/apache/skywalking">Apache Skywalking(Incubator)&lt;/a> 专门为微服务架构和云原生架构系统而设计并且支持分布式链路追踪的APM系统。&lt;a href="https://github.com/apache/skywalking">Apache Skywalking(Incubator)&lt;/a>通过加载探针的方式收集应用调用链路信息,并对采集的调用链路信息进行分析,生成应用间关系和服务间关系以及服务指标。&lt;a href="https://github.com/apache/skywalking">Apache Skywalking (Incubating)&lt;/a>目前支持多种语言,其中包括&lt;a href="https://github.com/apache/skywalking">Java&lt;/a>,&lt;a href="https://github.com/OpenSkywalking/skywalking-netcore">.Net Core&lt;/a>,&lt;a href="https://github.com/OpenSkywalking/skywalking-nodejs">Node.js&lt;/a>和&lt;a href="https://github.com/SkyAPM/go2sky">Go&lt;/a>语言。&lt;/p>
&lt;p>目前Skywalking已经支持从6个可视化维度剖析分布式系统的运行情况。总览视图是应用和组件的全局视图,其中包括组件和应用数量,应用的告警波动,慢服务列表以及应用吞吐量;拓扑图从应用依赖关系出发,展现整个应用的拓扑关系;应用视图则是从单个应用的角度,展现应用的上下游关系,TopN的服务和服务器,JVM的相关信息以及对应的主机信息。服务视图关注单个服务入口的运行情况以及此服务的上下游依赖关系,依赖度,帮助用户针对单个服务的优化和监控;调用链展现了调用的单次请求经过的所有埋点以及每个埋点的执行时长;告警视图根据配置阈值针对应用、服务器、服务进行实时告警。&lt;/p>
&lt;h2 id="dubbo与apache-skywalkingincubator">Dubbo与Apache Skywalking(Incubator)&lt;/h2>
&lt;h3 id="编写dubbo示例程序">编写Dubbo示例程序&lt;/h3>
&lt;p>Dubbo实例程序已上传到&lt;a href="https://github.com/SkywalkingTest/dubbo-trace-example">Github仓库&lt;/a>中。方便大家下载使用。&lt;/p>
&lt;h4 id="api工程">API工程&lt;/h4>
&lt;p>服务接口:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> org.apache.skywalking.demo.interfaces;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>public &lt;span style="color:#268bd2">interface&lt;/span> HelloService {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHello&lt;/span>(String name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="dubbo服务提供工程">Dubbo服务提供工程&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> org.apache.skywalking.demo.provider;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>@&lt;span style="color:#268bd2">Service&lt;/span>(version = &lt;span style="color:#2aa198">&amp;#34;${demo.service.version}&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> application = &lt;span style="color:#2aa198">&amp;#34;${dubbo.application.id}&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> protocol = &lt;span style="color:#2aa198">&amp;#34;${dubbo.protocol.id}&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registry = &lt;span style="color:#2aa198">&amp;#34;${dubbo.registry.id}&amp;#34;&lt;/span>, timeout = &lt;span style="color:#2aa198">60000&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>public class HelloServiceImpl implements HelloService {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> public String &lt;span style="color:#268bd2">sayHello&lt;/span>(String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> LockSupport.&lt;span style="color:#268bd2">parkNanos&lt;/span>(TimeUnit.SECONDS.&lt;span style="color:#268bd2">toNanos&lt;/span>(&lt;span style="color:#2aa198">1&lt;/span>));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;Hello, &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="consumer工程">Consumer工程&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> org.apache.skywalking.demo.consumer;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>@RestController
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>public class ConsumerController {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> private static &lt;span style="color:#dc322f">int&lt;/span> COUNT = &lt;span style="color:#2aa198">0&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @&lt;span style="color:#268bd2">Reference&lt;/span>(version = &lt;span style="color:#2aa198">&amp;#34;${demo.service.version}&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> application = &lt;span style="color:#2aa198">&amp;#34;${dubbo.application.id}&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> url = &lt;span style="color:#2aa198">&amp;#34;dubbo://localhost:20880&amp;#34;&lt;/span>, timeout = &lt;span style="color:#2aa198">60000&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> private HelloService helloService;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @&lt;span style="color:#268bd2">GetMapping&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;/sayHello/{name}&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> public String &lt;span style="color:#268bd2">sayHello&lt;/span>(@&lt;span style="color:#268bd2">PathVariable&lt;/span>(name = &lt;span style="color:#2aa198">&amp;#34;name&amp;#34;&lt;/span>) String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> ((COUNT&lt;span style="color:#719e07">++&lt;/span>) &lt;span style="color:#719e07">%&lt;/span> &lt;span style="color:#2aa198">3&lt;/span> &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>){
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> throw new &lt;span style="color:#268bd2">RuntimeException&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> LockSupport.&lt;span style="color:#268bd2">parkNanos&lt;/span>(TimeUnit.SECONDS.&lt;span style="color:#268bd2">toNanos&lt;/span>(&lt;span style="color:#2aa198">2&lt;/span>));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> helloService.&lt;span style="color:#268bd2">sayHello&lt;/span>(name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="部署apache-skywalkingincubator">部署Apache Skywalking(Incubator)&lt;/h3>
&lt;p>Apache Skywalking(Incubator)共提供两种部署模式:单节点模式和集群模式,以下为单节点模式部署步骤,集群模式部署详情参考&lt;a href="https://skywalking.apache.org/docs/main/next/en/setup/backend/backend-setup/">文档&lt;/a>。&lt;/p>
&lt;h4 id="依赖第三方组件">依赖第三方组件&lt;/h4>
&lt;ol>
&lt;li>JDK8+&lt;/li>
&lt;li>Elasticsearch 5.x&lt;/li>
&lt;/ol>
&lt;h4 id="部署步骤">部署步骤&lt;/h4>
&lt;ol>
&lt;li>下载&lt;a href="http://skywalking.apache.org/downloads/"> Apache Skywalking Collector&lt;/a>&lt;/li>
&lt;li>部署ElasticSearch
&lt;ul>
&lt;li>修改elasticsearch.yml文件,并设置&lt;code>cluster.name&lt;/code>设置成&lt;code>CollectorDBCluster&lt;/code>。此名称需要和collector配置文件一致。&lt;/li>
&lt;li>修改ES配置&lt;code>network.host&lt;/code>值,将&lt;code>network.host&lt;/code>的值修改成&lt;code>0.0.0.0&lt;/code>。&lt;/li>
&lt;li>启动Elasticsearch&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>解压并启动Skywalking Collector。运行&lt;code>bin/startup.sh&lt;/code>命令即可启动Skywalking Collector&lt;/li>
&lt;/ol>
&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-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>./mvnw clean package
&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>java -jar -javaagent:$AGENT_PATH/skywalking-agent.jar -Dskywalking.agent.application_code=dubbo-provider -Dskywalking.collector.servers=localhost:10800 dubbo-provider/target/dubbo-provider.jar
&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>java -jar -javaagent:$AGENT_PATH/skywalking-agent.jar -Dskywalking.agent.application_code=dubbo-consumer -Dskywalking.collector.servers=localhost:10800 dubbo-consumer/target/dubbo-consumer.jar
&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>curl http://localhost:8080/sayHello/test
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="skywalking监控截图">Skywalking监控截图:&lt;/h2>
&lt;h3 id="首页">首页&lt;/h3>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/skywalking-dashboard.png" alt="/admin-guide/images/skywalking-dashboard.png">&lt;/p>
&lt;h3 id="拓扑图">拓扑图&lt;/h3>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/skywalking-topology.png" alt="/admin-guide/images/skywalking-topology.png">&lt;/p>
&lt;h3 id="应用视图">应用视图&lt;/h3>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/skywalking-application.png" alt="/admin-guide/images/skywalking-application.png">&lt;/p>
&lt;p>JVM信息
&lt;img src="https://dubbo.apache.org/imgs/blog/skywalking-application_instance.png" alt="/admin-guide/images/skywalking-application_instance.png">&lt;/p>
&lt;h3 id="服务视图">服务视图&lt;/h3>
&lt;p>服务消费端:
&lt;img src="https://dubbo.apache.org/imgs/blog/skywalking-service-consumer.png" alt="/admin-guide/images/skywalking-service-consumer.png">&lt;/p>
&lt;p>服务提供端:
&lt;img src="https://dubbo.apache.org/imgs/blog/skywalking-service-provider.png" alt="/admin-guide/images/skywalking-service-provider.png">&lt;/p>
&lt;h3 id="trace视图">Trace视图&lt;/h3>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/skywalking-trace.png" alt="/admin-guide/images/skywalking-trace.png">&lt;/p>
&lt;p>Span信息:
&lt;img src="https://dubbo.apache.org/imgs/blog/skywalking-span-Info.png" alt="/admin-guide/images/skywalking-span-Info.png">&lt;/p>
&lt;h3 id="告警视图">告警视图&lt;/h3>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/skywalking-alarm.png" alt="/admin-guide/images/skywalking-alarm.png">&lt;/p></description></item><item><title>Blog: 当Dubbo遇上Arthas:排查问题的实践</title><link>https://dubbo.apache.org/zh-cn/blog/2019/02/02/%E5%BD%93dubbo%E9%81%87%E4%B8%8Aarthas%E6%8E%92%E6%9F%A5%E9%97%AE%E9%A2%98%E7%9A%84%E5%AE%9E%E8%B7%B5/</link><pubDate>Sat, 02 Feb 2019 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2019/02/02/%E5%BD%93dubbo%E9%81%87%E4%B8%8Aarthas%E6%8E%92%E6%9F%A5%E9%97%AE%E9%A2%98%E7%9A%84%E5%AE%9E%E8%B7%B5/</guid><description>
&lt;p>Apache Dubbo是Alibaba开源的高性能RPC框架,在国内有非常多的用户。&lt;/p>
&lt;ul>
&lt;li>Github: &lt;a href="https://github.com/apache/dubbo">https://github.com/apache/dubbo&lt;/a>&lt;/li>
&lt;li>文档:http://dubbo.apache.org/zh-cn/&lt;/li>
&lt;/ul>
&lt;p>Arthas是Alibaba开源的应用诊断利器,9月份开源以来,Github Star数三个月超过6000。&lt;/p>
&lt;ul>
&lt;li>Github: &lt;a href="https://github.com/alibaba/arthas">https://github.com/alibaba/arthas&lt;/a>&lt;/li>
&lt;li>文档:https://arthas.aliyun.com/doc/&lt;/li>
&lt;li>Arthas开源交流QQ群: 916328269&lt;/li>
&lt;li>Arthas开源交流钉钉群: 21965291&lt;/li>
&lt;/ul>
&lt;p>当Dubbo遇上Arthas,会碰撞出什么样的火花呢?下面来分享Arthas排查Dubbo问题的一些经验。&lt;/p>
&lt;h3 id="dubbo-arthas-demo">dubbo-arthas-demo&lt;/h3>
&lt;p>下面的排查分享基于这个&lt;code>dubbo-arthas-demo&lt;/code>,非常简单的一个应用,浏览器请求从Spring MVC到Dubbo Client,再发送到Dubbo Server。&lt;/p>
&lt;p>Demo里有两个spring boot应用,可以先启动&lt;code>server-demo&lt;/code>,再启动&lt;code>client-demo&lt;/code>。&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/hengyunabc/dubbo-arthas-demo">https://github.com/hengyunabc/dubbo-arthas-demo&lt;/a>&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span> /user/{id} -&amp;gt; UserService -&amp;gt; UserServiceImpl
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Browser Dubbo Client Dubbo Server
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Client端:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@RestController&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">UserController&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/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>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">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 style="color:#268bd2">@GetMapping&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;/user/{id}&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> User &lt;span style="color:#268bd2">findUserById&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#268bd2">@PathVariable&lt;/span> Integer 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>findUser&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;/code>&lt;/pre>&lt;/div>&lt;p>Server端:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Service&lt;/span>&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">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">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> User &lt;span style="color:#268bd2">findUser&lt;/span>&lt;span style="color:#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> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>id &lt;span style="color:#719e07">&amp;lt;&lt;/span> 1&lt;span style="color:#719e07">)&lt;/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;user id &amp;lt; 1, 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:#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>User user &lt;span style="color:#719e07">:&lt;/span> users&lt;span style="color:#719e07">)&lt;/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>user&lt;span style="color:#719e07">.&lt;/span>getId&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">==&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> &lt;span style="color:#719e07">return&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;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RuntimeException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Can not find user, 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:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="arthas快速开始">Arthas快速开始&lt;/h3>
&lt;ul>
&lt;li>&lt;a href="https://arthas.aliyun.com/doc/install-detail.html">https://arthas.aliyun.com/doc/install-detail.html&lt;/a>&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>$ wget https://arthas.aliyun.com/arthas-boot.jar
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$ java -jar arthas-boot.jar
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>启动后,会列出所有的java进程,选择1,然后回车,就会连接上&lt;code>ServerDemoApplication&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;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>$ java -jar arthas-boot.jar
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>* &lt;span style="color:#719e07">[&lt;/span>1&lt;span style="color:#719e07">]&lt;/span>: &lt;span style="color:#2aa198">43523&lt;/span> ServerDemoApplication
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">[&lt;/span>2&lt;span style="color:#719e07">]&lt;/span>: &lt;span style="color:#2aa198">22342&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">[&lt;/span>3&lt;span style="color:#719e07">]&lt;/span>: &lt;span style="color:#2aa198">44108&lt;/span> ClientDemoApplication
&lt;/span>&lt;/span>&lt;span style="display:flex;">&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">[&lt;/span>INFO&lt;span style="color:#719e07">]&lt;/span> arthas home: /Users/hengyunabc/.arthas/lib/3.0.5/arthas
&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> Try to attach process &lt;span style="color:#2aa198">43523&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> Attach process &lt;span style="color:#2aa198">43523&lt;/span> 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> arthas-client connect 127.0.0.1 &lt;span style="color:#2aa198">3658&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ,---. ,------. ,--------.,--. ,--. ,---. ,---.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> / O &lt;span style="color:#cb4b16">\ &lt;/span>| .--. &lt;span style="color:#2aa198">&amp;#39;&amp;#39;&lt;/span>--. .--&lt;span style="color:#2aa198">&amp;#39;| &amp;#39;&lt;/span>--&lt;span style="color:#2aa198">&amp;#39; | / O \ &amp;#39;&lt;/span> .-&lt;span style="color:#2aa198">&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">| .-. || &amp;#39;&lt;/span>--&lt;span style="color:#2aa198">&amp;#39;.&amp;#39;&lt;/span> | | | .--. &lt;span style="color:#719e07">||&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:#719e07">||&lt;/span> |&lt;span style="color:#cb4b16">\ &lt;/span> &lt;span style="color:#cb4b16">\ &lt;/span> | | | | | &lt;span style="color:#719e07">||&lt;/span> | | |.-&lt;span style="color:#2aa198">&amp;#39; |
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">`--&amp;#39;&lt;/span> &lt;span style="color:#586e75">`&lt;/span>--&lt;span style="color:#2aa198">&amp;#39;`--&amp;#39;&lt;/span> &lt;span style="color:#2aa198">&amp;#39;--&amp;#39;&lt;/span> &lt;span style="color:#586e75">`&lt;/span>--&lt;span style="color:#2aa198">&amp;#39; `--&amp;#39;&lt;/span> &lt;span style="color:#586e75">`&lt;/span>--&lt;span style="color:#2aa198">&amp;#39;`--&amp;#39;&lt;/span> &lt;span style="color:#586e75">`&lt;/span>--&lt;span style="color:#2aa198">&amp;#39;`-----&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>wiki: https://arthas.aliyun.com/doc
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>version: 3.0.5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>pid: &lt;span style="color:#2aa198">43523&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>time: 2018-12-05 16:23:52
&lt;/span>&lt;/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="dubbo线上服务抛出异常怎么获取调用参数">Dubbo线上服务抛出异常,怎么获取调用参数?&lt;/h3>
&lt;ul>
&lt;li>&lt;a href="https://arthas.aliyun.com/doc/watch.html">https://arthas.aliyun.com/doc/watch.html&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>当线上服务抛出异常时,最着急的是什么参数导致了抛异常?&lt;/p>
&lt;p>在demo里,访问 http://localhost:8080/user/0 ,&lt;code>UserServiceImpl&lt;/code>就会抛出一个异常,因为user id不合法。&lt;/p>
&lt;p>在Arthas里执行 &lt;code>watch com.example.UserService * -e -x 2 '{params,throwExp}'&lt;/code> ,然后再次访问,就可以看到watch命令把参数和异常都打印出来了。&lt;/p>
&lt;div class="highlight">&lt;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>$ watch com.example.UserService * -e -x 2 &amp;#39;{params,throwExp}&amp;#39;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Press Ctrl+C to abort.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Affect(class-cnt:1 , method-cnt:4) cost in 230 ms.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ts=2018-12-05 16:26:44; [cost=3.905523ms] result=@ArrayList[
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @Object[][
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @Integer[0],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> java.lang.IllegalArgumentException: user id &amp;lt; 1, id: 0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at com.example.UserServiceImpl.findUser(UserServiceImpl.java:24)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at com.alibaba.dubbo.common.bytecode.Wrapper1.invokeMethod(Wrapper1.java)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory$1.doInvoke(JavassistProxyFactory.java:45)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at com.alibaba.dubbo.rpc.proxy.AbstractProxyInvoker.invoke(AbstractProxyInvoker.java:71)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at com.alibaba.dubbo.config.invoker.DelegateProviderMetaDataInvoker.invoke(DelegateProviderMetaDataInvoker.java:48)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at com.alibaba.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:52)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at com.alibaba.dubbo.rpc.filter.ExceptionFilter.invoke(ExceptionFilter.java:61)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="怎样线上调试dubbo服务代码">怎样线上调试Dubbo服务代码?&lt;/h3>
&lt;ul>
&lt;li>&lt;a href="https://arthas.aliyun.com/doc/redefine.html">https://arthas.aliyun.com/doc/redefine.html&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>在本地开发时,可能会用到热部署工具,直接改代码,不需要重启应用。但是在线上环境,有没有办法直接动态调试代码?比如增加日志。&lt;/p>
&lt;p>在Arthas里,可以通过&lt;code>redefine&lt;/code>命令来达到线上不重启,动态更新代码的效果。&lt;/p>
&lt;p>比如我们修改下&lt;code>UserServiceImpl&lt;/code>,用&lt;code>System.out&lt;/code>打印出具体的&lt;code>User&lt;/code>对象来:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> User &lt;span style="color:#268bd2">findUser&lt;/span>&lt;span style="color:#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> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>id &lt;span style="color:#719e07">&amp;lt;&lt;/span> 1&lt;span style="color:#719e07">)&lt;/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;user id &amp;lt; 1, 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:#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>User user &lt;span style="color:#719e07">:&lt;/span> users&lt;span style="color:#719e07">)&lt;/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>user&lt;span style="color:#719e07">.&lt;/span>getId&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">==&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>user&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&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;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RuntimeException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Can not find user, 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:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>本地编绎后,把&lt;code>server-demo/target/classes/com/example/UserServiceImpl.class&lt;/code>传到线上服务器,然后用&lt;code>redefine&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>$ redefine -p /tmp/UserServiceImpl.class
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>redefine success, size: 1
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这样子更新成功之后,访问 http://localhost:8080/user/1 ,在&lt;code>ServerDemoApplication&lt;/code>的控制台里就可以看到打印出了user信息。&lt;/p>
&lt;h3 id="怎样动态修改dubbo的logger级别">怎样动态修改Dubbo的logger级别?&lt;/h3>
&lt;ul>
&lt;li>&lt;a href="https://arthas.aliyun.com/doc/ognl.html">https://arthas.aliyun.com/doc/ognl.html&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://arthas.aliyun.com/doc/sc.html">https://arthas.aliyun.com/doc/sc.html&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://commons.apache.org/proper/commons-ognl/language-guide.html">https://commons.apache.org/proper/commons-ognl/language-guide.html&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>在排查问题时,需要查看到更多的信息,如果可以把logger级别修改为&lt;code>DEBUG&lt;/code>,就非常有帮助。&lt;/p>
&lt;p>&lt;code>ognl&lt;/code>是apache开源的一个轻量级表达式引擎。下面通过Arthas里的&lt;code>ognl&lt;/code>命令来动态修改logger级别。&lt;/p>
&lt;p>首先获取Dubbo里&lt;code>TraceFilter&lt;/code>的一个logger对象,看下它的实现类,可以发现是log4j。&lt;/p>
&lt;div class="highlight">&lt;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>$ ognl &lt;span style="color:#2aa198">&amp;#39;@com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter@logger.logger&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>@Log4jLogger&lt;span style="color:#719e07">[&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">FQCN&lt;/span>&lt;span style="color:#719e07">=&lt;/span>@String&lt;span style="color:#719e07">[&lt;/span>com.alibaba.dubbo.common.logger.support.FailsafeLogger&lt;span style="color:#719e07">]&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">logger&lt;/span>&lt;span style="color:#719e07">=&lt;/span>@Logger&lt;span style="color:#719e07">[&lt;/span>org.apache.log4j.Logger@2f19bdcf&lt;span style="color:#719e07">]&lt;/span>,
&lt;/span>&lt;/span>&lt;span 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>sc&lt;/code>命令来查看具体从哪个jar包里加载的:&lt;/p>
&lt;div class="highlight">&lt;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>$ sc -d org.apache.log4j.Logger
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> class-info org.apache.log4j.Logger
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> code-source /Users/hengyunabc/.m2/repository/org/slf4j/log4j-over-slf4j/1.7.25/log4j-over-slf4j-1.7.25.jar
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name org.apache.log4j.Logger
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> isInterface &lt;span style="color:#b58900">false&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> isAnnotation &lt;span style="color:#b58900">false&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> isEnum &lt;span style="color:#b58900">false&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> isAnonymousClass &lt;span style="color:#b58900">false&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> isArray &lt;span style="color:#b58900">false&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> isLocalClass &lt;span style="color:#b58900">false&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> isMemberClass &lt;span style="color:#b58900">false&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> isPrimitive &lt;span style="color:#b58900">false&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> isSynthetic &lt;span style="color:#b58900">false&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> simple-name Logger
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> modifier public
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> annotation
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> interfaces
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> super-class +-org.apache.log4j.Category
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> +-java.lang.Object
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> class-loader +-sun.misc.Launcher&lt;span style="color:#268bd2">$AppClassLoader&lt;/span>@5c647e05
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> +-sun.misc.Launcher&lt;span style="color:#268bd2">$ExtClassLoader&lt;/span>@59878d35
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> classLoaderHash 5c647e05
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Affect&lt;span style="color:#719e07">(&lt;/span>row-cnt:1&lt;span style="color:#719e07">)&lt;/span> cost in &lt;span style="color:#2aa198">126&lt;/span> ms.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>可以看到log4j是通过slf4j代理的。&lt;/strong>&lt;/p>
&lt;p>那么通过&lt;code>org.slf4j.LoggerFactory&lt;/code>获取&lt;code>root&lt;/code> logger,再修改它的level:&lt;/p>
&lt;div class="highlight">&lt;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>$ ognl &amp;#39;@org.slf4j.LoggerFactory@getLogger(&amp;#34;root&amp;#34;).setLevel(@ch.qos.logback.classic.Level@DEBUG)&amp;#39;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>null
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$ ognl &amp;#39;@org.slf4j.LoggerFactory@getLogger(&amp;#34;root&amp;#34;).getLevel().toString()&amp;#39;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>@String[DEBUG]
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可以看到修改之后,&lt;code>root&lt;/code> logger的level变为&lt;code>DEBUG&lt;/code>。&lt;/p>
&lt;h3 id="怎样减少测试小姐姐重复发请求的麻烦">怎样减少测试小姐姐重复发请求的麻烦?&lt;/h3>
&lt;ul>
&lt;li>&lt;a href="https://arthas.aliyun.com/doc/tt.html">https://arthas.aliyun.com/doc/tt.html&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>在平时开发时,可能需要测试小姐姐发请求过来联调,但是我们在debug时,可能不小心直接跳过去了。这样子就尴尬了,需要测试小姐姐再发请求过来。&lt;/p>
&lt;p>Arthas里提供了&lt;code>tt&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>$ tt -t com.example.UserServiceImpl findUser
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Press Ctrl+C to abort.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Affect&lt;span style="color:#719e07">(&lt;/span>class-cnt:1 , method-cnt:1&lt;span style="color:#719e07">)&lt;/span> cost in &lt;span style="color:#2aa198">145&lt;/span> ms.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> INDEX TIMESTAMP COST&lt;span style="color:#719e07">(&lt;/span>ms&lt;span style="color:#719e07">)&lt;/span> IS-RET IS-EXP OBJECT CLASS METHOD
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>----------------------------------------------------------------------------------------------------------------
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">1000&lt;/span> 2018-12-05 17:47:52 1.56523 &lt;span style="color:#b58900">true&lt;/span> &lt;span style="color:#b58900">false&lt;/span> 0x3233483 UserServiceImpl findUser
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">1001&lt;/span> 2018-12-05 17:48:03 0.286176 &lt;span style="color:#b58900">false&lt;/span> &lt;span style="color:#b58900">true&lt;/span> 0x3233483 UserServiceImpl findUser
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">1002&lt;/span> 2018-12-05 17:48:11 90.324335 &lt;span style="color:#b58900">true&lt;/span> &lt;span style="color:#b58900">false&lt;/span> 0x3233483 UserServiceImpl findUser
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>上面的&lt;code>tt -t&lt;/code>命令捕获到了3个请求。然后通过&lt;code>tt --play&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>$ tt --play -i &lt;span style="color:#2aa198">1000&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RE-INDEX &lt;span style="color:#2aa198">1000&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> GMT-REPLAY 2018-12-05 17:55:50
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> OBJECT 0x3233483
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CLASS com.example.UserServiceImpl
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> METHOD findUser
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> PARAMETERS&lt;span style="color:#719e07">[&lt;/span>0&lt;span style="color:#719e07">]&lt;/span> @Integer&lt;span style="color:#719e07">[&lt;/span>1&lt;span style="color:#719e07">]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> IS-RETURN &lt;span style="color:#b58900">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> IS-EXCEPTION &lt;span style="color:#b58900">false&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RETURN-OBJ @User&lt;span style="color:#719e07">[&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">id&lt;/span>&lt;span style="color:#719e07">=&lt;/span>@Integer&lt;span style="color:#719e07">[&lt;/span>1&lt;span style="color:#719e07">]&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>&lt;span style="color:#719e07">=&lt;/span>@String&lt;span style="color:#719e07">[&lt;/span>Deanna Borer&lt;span style="color:#719e07">]&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Time fragment&lt;span style="color:#719e07">[&lt;/span>1000&lt;span style="color:#719e07">]&lt;/span> successfully replayed.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Affect&lt;span style="color:#719e07">(&lt;/span>row-cnt:1&lt;span style="color:#719e07">)&lt;/span> cost in &lt;span style="color:#2aa198">4&lt;/span> ms.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="dubbo运行时有哪些filter-耗时是多少">Dubbo运行时有哪些Filter? 耗时是多少?&lt;/h3>
&lt;ul>
&lt;li>&lt;a href="https://arthas.aliyun.com/doc/trace.html">https://arthas.aliyun.com/doc/trace.html&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>Dubbo运行时会加载很多的Filter,那么一个请求会经过哪些Filter处理,Filter里的耗时又是多少呢?&lt;/p>
&lt;p>通过Arthas的&lt;code>trace&lt;/code>命令,可以很方便地知道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-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>$ trace com.alibaba.dubbo.rpc.Filter *
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Press Ctrl+C to abort.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Affect&lt;span style="color:#719e07">(&lt;/span>class-cnt:19 , method-cnt:59&lt;span style="color:#719e07">)&lt;/span> cost in &lt;span style="color:#2aa198">1441&lt;/span> ms.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">`&lt;/span>---ts&lt;span style="color:#719e07">=&lt;/span>2018-12-05 19:07:26;&lt;span style="color:#268bd2">thread_name&lt;/span>&lt;span style="color:#719e07">=&lt;/span>DubboServerHandler-30.5.125.152:20880-thread-10;&lt;span style="color:#268bd2">id&lt;/span>&lt;span style="color:#719e07">=&lt;/span>3e;&lt;span style="color:#268bd2">is_daemon&lt;/span>&lt;span style="color:#719e07">=&lt;/span>true;&lt;span style="color:#268bd2">priority&lt;/span>&lt;span style="color:#719e07">=&lt;/span>5;&lt;span style="color:#268bd2">TCCL&lt;/span>&lt;span style="color:#719e07">=&lt;/span>sun.misc.Launcher&lt;span style="color:#268bd2">$AppClassLoader&lt;/span>@5c647e05
&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>8.435844ms&lt;span style="color:#719e07">]&lt;/span> com.alibaba.dubbo.rpc.filter.EchoFilter: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>0.124572ms&lt;span style="color:#719e07">]&lt;/span> com.alibaba.dubbo.rpc.Invocation:getMethodName&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> +---&lt;span style="color:#719e07">[&lt;/span>0.065123ms&lt;span style="color:#719e07">]&lt;/span> java.lang.String:equals&lt;span style="color:#719e07">()&lt;/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>7.762928ms&lt;span style="color:#719e07">]&lt;/span> com.alibaba.dubbo.rpc.Invoker:invoke&lt;span style="color:#719e07">()&lt;/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>7.494124ms&lt;span style="color:#719e07">]&lt;/span> com.alibaba.dubbo.rpc.filter.ClassLoaderFilter: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:#268bd2">min&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0.00355ms,max&lt;span style="color:#719e07">=&lt;/span>0.049922ms,total&lt;span style="color:#719e07">=&lt;/span>0.057637ms,count&lt;span style="color:#719e07">=&lt;/span>3&lt;span style="color:#719e07">]&lt;/span> java.lang.Thread:currentThread&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> +---&lt;span style="color:#719e07">[&lt;/span>0.0126ms&lt;span style="color:#719e07">]&lt;/span> java.lang.Thread:getContextClassLoader&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> +---&lt;span style="color:#719e07">[&lt;/span>0.02188ms&lt;span style="color:#719e07">]&lt;/span> com.alibaba.dubbo.rpc.Invoker: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>0.004115ms&lt;span style="color:#719e07">]&lt;/span> java.lang.Class:getClassLoader&lt;span style="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">min&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0.003906ms,max&lt;span style="color:#719e07">=&lt;/span>0.014058ms,total&lt;span style="color:#719e07">=&lt;/span>0.017964ms,count&lt;span style="color:#719e07">=&lt;/span>2&lt;span style="color:#719e07">]&lt;/span> java.lang.Thread:setContextClassLoader&lt;span style="color:#719e07">()&lt;/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>7.033486ms&lt;span style="color:#719e07">]&lt;/span> com.alibaba.dubbo.rpc.Invoker:invoke&lt;span style="color:#719e07">()&lt;/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>6.869488ms&lt;span style="color:#719e07">]&lt;/span> com.alibaba.dubbo.rpc.filter.GenericFilter: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>0.01481ms&lt;span style="color:#719e07">]&lt;/span> com.alibaba.dubbo.rpc.Invocation:getMethodName&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="dubbo动态代理是怎样实现的">Dubbo动态代理是怎样实现的?&lt;/h3>
&lt;ul>
&lt;li>&lt;a href="https://arthas.aliyun.com/doc/jad.html">https://arthas.aliyun.com/doc/jad.html&lt;/a>&lt;/li>
&lt;li>com.alibaba.dubbo.common.bytecode.Wrapper&lt;/li>
&lt;/ul>
&lt;p>通过Arthas的&lt;code>jad&lt;/code>命令,可以看到Dubbo通过javaassist动态生成的Wrappr类的代码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>$ jad com&lt;span style="color:#719e07">.&lt;/span>alibaba&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>common&lt;span style="color:#719e07">.&lt;/span>bytecode&lt;span style="color:#719e07">.&lt;/span>Wrapper1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ClassLoader:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">+-&lt;/span>sun&lt;span style="color:#719e07">.&lt;/span>misc&lt;span style="color:#719e07">.&lt;/span>Launcher$AppClassLoader&lt;span style="color:#268bd2">@5c647e05&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">+-&lt;/span>sun&lt;span style="color:#719e07">.&lt;/span>misc&lt;span style="color:#719e07">.&lt;/span>Launcher$ExtClassLoader&lt;span style="color:#268bd2">@59878d35&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Location:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">/&lt;/span>Users&lt;span style="color:#719e07">/&lt;/span>hengyunabc&lt;span style="color:#719e07">/.&lt;/span>m2&lt;span style="color:#719e07">/&lt;/span>repository&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>2&lt;span style="color:#719e07">.&lt;/span>5&lt;span style="color:#719e07">.&lt;/span>10&lt;span style="color:#719e07">/&lt;/span>dubbo&lt;span style="color:#719e07">-&lt;/span>2&lt;span style="color:#719e07">.&lt;/span>5&lt;span style="color:#719e07">.&lt;/span>10&lt;span style="color:#719e07">.&lt;/span>jar
&lt;/span>&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> com.alibaba.dubbo.common.bytecode&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/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">Wrapper1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">extends&lt;/span> Wrapper
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">implements&lt;/span> ClassGenerator&lt;span style="color:#719e07">.&lt;/span>DC &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/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> Object &lt;span style="color:#268bd2">invokeMethod&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Object object&lt;span style="color:#719e07">,&lt;/span> String string&lt;span style="color:#719e07">,&lt;/span> Class&lt;span style="color:#719e07">[]&lt;/span> arrclass&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">[]&lt;/span> arrobject&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> InvocationTargetException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> UserServiceImpl userServiceImpl&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> userServiceImpl &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>UserServiceImpl&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">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Throwable throwable&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException&lt;span style="color:#719e07">(&lt;/span>throwable&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;findUser&amp;#34;&lt;/span>&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>string&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> arrclass&lt;span style="color:#719e07">.&lt;/span>length &lt;span style="color:#719e07">==&lt;/span> 1&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> userServiceImpl&lt;span style="color:#719e07">.&lt;/span>findUser&lt;span style="color:#719e07">(((&lt;/span>Number&lt;span style="color:#719e07">)&lt;/span>arrobject&lt;span style="color:#719e07">[&lt;/span>0&lt;span style="color:#719e07">]).&lt;/span>intValue&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;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:#2aa198">&amp;#34;listUsers&amp;#34;&lt;/span>&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>string&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> arrclass&lt;span style="color:#719e07">.&lt;/span>length &lt;span style="color:#719e07">==&lt;/span> 0&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> userServiceImpl&lt;span style="color:#719e07">.&lt;/span>listUsers&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;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:#2aa198">&amp;#34;findUserByName&amp;#34;&lt;/span>&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>string&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> arrclass&lt;span style="color:#719e07">.&lt;/span>length &lt;span style="color:#719e07">==&lt;/span> 1&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> userServiceImpl&lt;span style="color:#719e07">.&lt;/span>findUserByName&lt;span style="color:#719e07">((&lt;/span>String&lt;span style="color:#719e07">)&lt;/span>arrobject&lt;span style="color:#719e07">[&lt;/span>0&lt;span style="color:#719e07">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;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="获取spring-context">获取Spring context&lt;/h3>
&lt;p>除了上面介绍的一些排查技巧,下面分享一个获取Spring Context,然后“为所欲为”的例子。&lt;/p>
&lt;p>在Dubbo里有一个扩展&lt;code>com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory&lt;/code>,把Spring Context保存到了里面。
因此,我们可以通过&lt;code>ognl&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>$ ognl &lt;span style="color:#2aa198">&amp;#39;#context=@com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory@contexts.iterator.next, #context.getBean(&amp;#34;userServiceImpl&amp;#34;).findUser(1)&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&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:#268bd2">id&lt;/span>&lt;span style="color:#719e07">=&lt;/span>@Integer&lt;span style="color:#719e07">[&lt;/span>1&lt;span style="color:#719e07">]&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>&lt;span style="color:#719e07">=&lt;/span>@String&lt;span style="color:#719e07">[&lt;/span>Deanna Borer&lt;span style="color:#719e07">]&lt;/span>,
&lt;/span>&lt;/span>&lt;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>SpringExtensionFactory@contexts.iterator.next&lt;/code> 获取到&lt;code>SpringExtensionFactory&lt;/code>里保存的spring context对象&lt;/li>
&lt;li>&lt;code>#context.getBean(&amp;quot;userServiceImpl&amp;quot;).findUser(1)&lt;/code> 获取到&lt;code>userServiceImpl&lt;/code>再执行一次调用&lt;/li>
&lt;/ul>
&lt;p>只要充分发挥想像力,组合Arthas里的各种命令,可以发挥出神奇的效果。&lt;/p>
&lt;h2 id="总结">总结&lt;/h2>
&lt;p>本篇文章来自杭州Dubbo Meetup的分享《当DUBBO遇上Arthas - 排查问题的实践》,希望对大家线上排查Dubbo问题有帮助。&lt;/p>
&lt;p>分享的PDF可以在 &lt;a href="https://github.com/alibaba/arthas/issues/327">https://github.com/alibaba/arthas/issues/327&lt;/a> 里下载。&lt;/p></description></item><item><title>Blog: 如何使用Seata保证Dubbo微服务间的一致性</title><link>https://dubbo.apache.org/zh-cn/blog/2019/01/17/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8seata%E4%BF%9D%E8%AF%81dubbo%E5%BE%AE%E6%9C%8D%E5%8A%A1%E9%97%B4%E7%9A%84%E4%B8%80%E8%87%B4%E6%80%A7/</link><pubDate>Thu, 17 Jan 2019 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2019/01/17/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8seata%E4%BF%9D%E8%AF%81dubbo%E5%BE%AE%E6%9C%8D%E5%8A%A1%E9%97%B4%E7%9A%84%E4%B8%80%E8%87%B4%E6%80%A7/</guid><description>
&lt;h2 id="案例">案例&lt;/h2>
&lt;p>用户采购商品业务,整个业务包含3个微服务:&lt;/p>
&lt;ul>
&lt;li>库存服务: 扣减给定商品的库存数量。&lt;/li>
&lt;li>订单服务: 根据采购请求生成订单。&lt;/li>
&lt;li>账户服务: 用户账户金额扣减。&lt;/li>
&lt;/ul>
&lt;h3 id="业务结构图">业务结构图&lt;/h3>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/fescar/fescar-1.png" alt="Architecture">&lt;/p>
&lt;h3 id="storageservice">StorageService&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">StorageService&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 扣除存储数量
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">deduct&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String commodityCode&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> count&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;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="orderservice">OrderService&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">OrderService&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 创建订单
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Order &lt;span style="color:#268bd2">create&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String userId&lt;span style="color:#719e07">,&lt;/span> String commodityCode&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> orderCount&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;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="accountservice">AccountService&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">AccountService&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 从用户账户中借出
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">debit&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String userId&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> money&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;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>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">BusinessServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> BusinessService &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> StorageService storageService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> OrderService orderService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 采购
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">purchase&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String userId&lt;span style="color:#719e07">,&lt;/span> String commodityCode&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> orderCount&lt;span style="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> storageService&lt;span style="color:#719e07">.&lt;/span>deduct&lt;span style="color:#719e07">(&lt;/span>commodityCode&lt;span style="color:#719e07">,&lt;/span> orderCount&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> orderService&lt;span style="color:#719e07">.&lt;/span>create&lt;span style="color:#719e07">(&lt;/span>userId&lt;span style="color:#719e07">,&lt;/span> commodityCode&lt;span style="color:#719e07">,&lt;/span> orderCount&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;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">StorageServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> StorageService &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> StorageDAO storageDAO&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">deduct&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String commodityCode&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> count&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Storage storage &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Storage&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> storage&lt;span style="color:#719e07">.&lt;/span>setCount&lt;span style="color:#719e07">(&lt;/span>count&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> storage&lt;span style="color:#719e07">.&lt;/span>setCommodityCode&lt;span style="color:#719e07">(&lt;/span>commodityCode&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> storageDAO&lt;span style="color:#719e07">.&lt;/span>update&lt;span style="color:#719e07">(&lt;/span>storage&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;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">OrderServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> OrderService &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> OrderDAO orderDAO&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> AccountService accountService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> Order &lt;span style="color:#268bd2">create&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String userId&lt;span style="color:#719e07">,&lt;/span> String commodityCode&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> orderCount&lt;span style="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:#dc322f">int&lt;/span> orderMoney &lt;span style="color:#719e07">=&lt;/span> calculate&lt;span style="color:#719e07">(&lt;/span>commodityCode&lt;span style="color:#719e07">,&lt;/span> orderCount&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> accountService&lt;span style="color:#719e07">.&lt;/span>debit&lt;span style="color:#719e07">(&lt;/span>userId&lt;span style="color:#719e07">,&lt;/span> orderMoney&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Order order &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Order&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> order&lt;span style="color:#719e07">.&lt;/span>userId &lt;span style="color:#719e07">=&lt;/span> userId&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> order&lt;span style="color:#719e07">.&lt;/span>commodityCode &lt;span style="color:#719e07">=&lt;/span> commodityCode&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> order&lt;span style="color:#719e07">.&lt;/span>count &lt;span style="color:#719e07">=&lt;/span> orderCount&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> order&lt;span style="color:#719e07">.&lt;/span>money &lt;span style="color:#719e07">=&lt;/span> orderMoney&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> orderDAO&lt;span style="color:#719e07">.&lt;/span>insert&lt;span style="color:#719e07">(&lt;/span>order&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;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="seata-分布式事务解决方案">Seata 分布式事务解决方案&lt;/h2>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/fescar/fescar-2.png" alt="undefined">&lt;/p>
&lt;p>此处仅仅需要一行注解 &lt;code>@GlobalTransactional&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>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@GlobalTransactional&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">purchase&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String userId&lt;span style="color:#719e07">,&lt;/span> String commodityCode&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> orderCount&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">......&lt;/span>
&lt;/span>&lt;/span>&lt;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="dubbo-与-seata-结合的例子">Dubbo 与 Seata 结合的例子&lt;/h2>
&lt;h3 id="step-1-安装数据库">Step 1: 安装数据库&lt;/h3>
&lt;ul>
&lt;li>要求: MySQL (InnoDB 存储引擎)。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>提示:&lt;/strong> 事实上例子中3个微服务需要3个独立的数据库,但为了方便我们使用同一物理库并配置3个逻辑连接串。&lt;/p>
&lt;p>更改以下xml文件中的数据库url、username和password&lt;/p>
&lt;p>dubbo-account-service.xml
dubbo-order-service.xml
dubbo-storage-service.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;property&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;url&amp;#34;&lt;/span> value=&lt;span style="color:#2aa198">&amp;#34;jdbc:mysql://x.x.x.x:3306/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:#268bd2">&amp;lt;property&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;username&amp;#34;&lt;/span> value=&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:#268bd2">&amp;lt;property&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;password&amp;#34;&lt;/span> value=&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;h3 id="step-2-为-seata-创建-undo_log-表">Step 2: 为 Seata 创建 undo_log 表&lt;/h3>
&lt;p>&lt;code>UNDO_LOG&lt;/code> 此表用于 Seata 的AT模式。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sql" data-lang="sql">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">-- 注意当 Seata 版本升级至 0.3.0+ 将由之前的普通索引变更为唯一索引。
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#719e07">CREATE&lt;/span> &lt;span style="color:#719e07">TABLE&lt;/span> &lt;span style="color:#719e07">`&lt;/span>undo_log&lt;span style="color:#719e07">`&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">`&lt;/span>id&lt;span style="color:#719e07">`&lt;/span> &lt;span style="color:#b58900">bigint&lt;/span>(&lt;span style="color:#2aa198">20&lt;/span>) &lt;span style="color:#719e07">NOT&lt;/span> &lt;span style="color:#719e07">NULL&lt;/span> AUTO_INCREMENT,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">`&lt;/span>branch_id&lt;span style="color:#719e07">`&lt;/span> &lt;span style="color:#b58900">bigint&lt;/span>(&lt;span style="color:#2aa198">20&lt;/span>) &lt;span style="color:#719e07">NOT&lt;/span> &lt;span style="color:#719e07">NULL&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">`&lt;/span>xid&lt;span style="color:#719e07">`&lt;/span> &lt;span style="color:#b58900">varchar&lt;/span>(&lt;span style="color:#2aa198">100&lt;/span>) &lt;span style="color:#719e07">NOT&lt;/span> &lt;span style="color:#719e07">NULL&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">`&lt;/span>context&lt;span style="color:#719e07">`&lt;/span> &lt;span style="color:#b58900">varchar&lt;/span>(&lt;span style="color:#2aa198">128&lt;/span>) &lt;span style="color:#719e07">NOT&lt;/span> &lt;span style="color:#719e07">NULL&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">`&lt;/span>rollback_info&lt;span style="color:#719e07">`&lt;/span> longblob &lt;span style="color:#719e07">NOT&lt;/span> &lt;span style="color:#719e07">NULL&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">`&lt;/span>log_status&lt;span style="color:#719e07">`&lt;/span> &lt;span style="color:#b58900">int&lt;/span>(&lt;span style="color:#2aa198">11&lt;/span>) &lt;span style="color:#719e07">NOT&lt;/span> &lt;span style="color:#719e07">NULL&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">`&lt;/span>log_created&lt;span style="color:#719e07">`&lt;/span> datetime &lt;span style="color:#719e07">NOT&lt;/span> &lt;span style="color:#719e07">NULL&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">`&lt;/span>log_modified&lt;span style="color:#719e07">`&lt;/span> datetime &lt;span style="color:#719e07">NOT&lt;/span> &lt;span style="color:#719e07">NULL&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">`&lt;/span>ext&lt;span style="color:#719e07">`&lt;/span> &lt;span style="color:#b58900">varchar&lt;/span>(&lt;span style="color:#2aa198">100&lt;/span>) &lt;span style="color:#719e07">DEFAULT&lt;/span> &lt;span style="color:#719e07">NULL&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">PRIMARY&lt;/span> &lt;span style="color:#719e07">KEY&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">UNIQUE&lt;/span> &lt;span style="color:#719e07">KEY&lt;/span> &lt;span style="color:#719e07">`&lt;/span>ux_undo_log&lt;span style="color:#719e07">`&lt;/span> (&lt;span style="color:#719e07">`&lt;/span>xid&lt;span style="color:#719e07">`&lt;/span>,&lt;span style="color:#719e07">`&lt;/span>branch_id&lt;span style="color:#719e07">`&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>) ENGINE&lt;span style="color:#719e07">=&lt;/span>InnoDB AUTO_INCREMENT&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">1&lt;/span> &lt;span style="color:#719e07">DEFAULT&lt;/span> CHARSET&lt;span style="color:#719e07">=&lt;/span>utf8;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="step-3-创建相关业务表">Step 3: 创建相关业务表&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-sql" data-lang="sql">&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">DROP&lt;/span> &lt;span style="color:#719e07">TABLE&lt;/span> &lt;span style="color:#719e07">IF&lt;/span> &lt;span style="color:#719e07">EXISTS&lt;/span> &lt;span style="color:#719e07">`&lt;/span>storage_tbl&lt;span style="color:#719e07">`&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">CREATE&lt;/span> &lt;span style="color:#719e07">TABLE&lt;/span> &lt;span style="color:#719e07">`&lt;/span>storage_tbl&lt;span style="color:#719e07">`&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">`&lt;/span>id&lt;span style="color:#719e07">`&lt;/span> &lt;span style="color:#b58900">int&lt;/span>(&lt;span style="color:#2aa198">11&lt;/span>) &lt;span style="color:#719e07">NOT&lt;/span> &lt;span style="color:#719e07">NULL&lt;/span> AUTO_INCREMENT,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">`&lt;/span>commodity_code&lt;span style="color:#719e07">`&lt;/span> &lt;span style="color:#b58900">varchar&lt;/span>(&lt;span style="color:#2aa198">255&lt;/span>) &lt;span style="color:#719e07">DEFAULT&lt;/span> &lt;span style="color:#719e07">NULL&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">count&lt;/span>&lt;span style="color:#719e07">`&lt;/span> &lt;span style="color:#b58900">int&lt;/span>(&lt;span style="color:#2aa198">11&lt;/span>) &lt;span style="color:#719e07">DEFAULT&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">PRIMARY&lt;/span> &lt;span style="color:#719e07">KEY&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">UNIQUE&lt;/span> &lt;span style="color:#719e07">KEY&lt;/span> (&lt;span style="color:#719e07">`&lt;/span>commodity_code&lt;span style="color:#719e07">`&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>) ENGINE&lt;span style="color:#719e07">=&lt;/span>InnoDB &lt;span style="color:#719e07">DEFAULT&lt;/span> CHARSET&lt;span style="color:#719e07">=&lt;/span>utf8;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&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">DROP&lt;/span> &lt;span style="color:#719e07">TABLE&lt;/span> &lt;span style="color:#719e07">IF&lt;/span> &lt;span style="color:#719e07">EXISTS&lt;/span> &lt;span style="color:#719e07">`&lt;/span>order_tbl&lt;span style="color:#719e07">`&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">CREATE&lt;/span> &lt;span style="color:#719e07">TABLE&lt;/span> &lt;span style="color:#719e07">`&lt;/span>order_tbl&lt;span style="color:#719e07">`&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">`&lt;/span>id&lt;span style="color:#719e07">`&lt;/span> &lt;span style="color:#b58900">int&lt;/span>(&lt;span style="color:#2aa198">11&lt;/span>) &lt;span style="color:#719e07">NOT&lt;/span> &lt;span style="color:#719e07">NULL&lt;/span> AUTO_INCREMENT,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">`&lt;/span>user_id&lt;span style="color:#719e07">`&lt;/span> &lt;span style="color:#b58900">varchar&lt;/span>(&lt;span style="color:#2aa198">255&lt;/span>) &lt;span style="color:#719e07">DEFAULT&lt;/span> &lt;span style="color:#719e07">NULL&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">`&lt;/span>commodity_code&lt;span style="color:#719e07">`&lt;/span> &lt;span style="color:#b58900">varchar&lt;/span>(&lt;span style="color:#2aa198">255&lt;/span>) &lt;span style="color:#719e07">DEFAULT&lt;/span> &lt;span style="color:#719e07">NULL&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">count&lt;/span>&lt;span style="color:#719e07">`&lt;/span> &lt;span style="color:#b58900">int&lt;/span>(&lt;span style="color:#2aa198">11&lt;/span>) &lt;span style="color:#719e07">DEFAULT&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">`&lt;/span>money&lt;span style="color:#719e07">`&lt;/span> &lt;span style="color:#b58900">int&lt;/span>(&lt;span style="color:#2aa198">11&lt;/span>) &lt;span style="color:#719e07">DEFAULT&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">PRIMARY&lt;/span> &lt;span style="color:#719e07">KEY&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>) ENGINE&lt;span style="color:#719e07">=&lt;/span>InnoDB &lt;span style="color:#719e07">DEFAULT&lt;/span> CHARSET&lt;span style="color:#719e07">=&lt;/span>utf8;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&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">DROP&lt;/span> &lt;span style="color:#719e07">TABLE&lt;/span> &lt;span style="color:#719e07">IF&lt;/span> &lt;span style="color:#719e07">EXISTS&lt;/span> &lt;span style="color:#719e07">`&lt;/span>account_tbl&lt;span style="color:#719e07">`&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">CREATE&lt;/span> &lt;span style="color:#719e07">TABLE&lt;/span> &lt;span style="color:#719e07">`&lt;/span>account_tbl&lt;span style="color:#719e07">`&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">`&lt;/span>id&lt;span style="color:#719e07">`&lt;/span> &lt;span style="color:#b58900">int&lt;/span>(&lt;span style="color:#2aa198">11&lt;/span>) &lt;span style="color:#719e07">NOT&lt;/span> &lt;span style="color:#719e07">NULL&lt;/span> AUTO_INCREMENT,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">`&lt;/span>user_id&lt;span style="color:#719e07">`&lt;/span> &lt;span style="color:#b58900">varchar&lt;/span>(&lt;span style="color:#2aa198">255&lt;/span>) &lt;span style="color:#719e07">DEFAULT&lt;/span> &lt;span style="color:#719e07">NULL&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">`&lt;/span>money&lt;span style="color:#719e07">`&lt;/span> &lt;span style="color:#b58900">int&lt;/span>(&lt;span style="color:#2aa198">11&lt;/span>) &lt;span style="color:#719e07">DEFAULT&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">PRIMARY&lt;/span> &lt;span style="color:#719e07">KEY&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>) ENGINE&lt;span style="color:#719e07">=&lt;/span>InnoDB &lt;span style="color:#719e07">DEFAULT&lt;/span> CHARSET&lt;span style="color:#719e07">=&lt;/span>utf8;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="step-4-启动-seata-server-服务">Step 4: 启动 Seata-Server 服务&lt;/h3>
&lt;ul>
&lt;li>下载&lt;a href="https://github.com/seata/seata/releases">服务器软件包&lt;/a>,将其解压缩。&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>Usage: sh seata-server.sh&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">for&lt;/span> linux and mac&lt;span style="color:#719e07">)&lt;/span> or cmd seata-server.bat&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">for&lt;/span> windows&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">[&lt;/span>options&lt;span style="color:#719e07">]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Options:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --host, -h
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> The host to bind.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Default: 0.0.0.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --port, -p
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> The port to listen.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Default: &lt;span style="color:#2aa198">8091&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --storeMode, -m
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log store mode : file、db
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Default: file
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --help
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>e.g.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sh seata-server.sh -p &lt;span style="color:#2aa198">8091&lt;/span> -h 127.0.0.1 -m file
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="step-5-运行例子">Step 5: 运行例子&lt;/h3>
&lt;ul>
&lt;li>启动账户服务 (&lt;a href="https://github.com/apache/dubbo-samples/blob/master/99-integration/dubbo-samples-transaction/src/main/java/org/apache/dubbo/samples/starter/DubboAccountServiceStarter.java">DubboAccountServiceStarter&lt;/a>).&lt;/li>
&lt;li>启动库存服务 (&lt;a href="https://github.com/apache/dubbo-samples/blob/master/99-integration/dubbo-samples-transaction/src/main/java/org/apache/dubbo/samples/starter/DubboStorageServiceStarter.java">DubboStorageServiceStarter&lt;/a>).&lt;/li>
&lt;li>启动订单服务 (&lt;a href="https://github.com/apache/dubbo-samples/blob/master/99-integration/dubbo-samples-transaction/src/main/java/org/apache/dubbo/samples/starter/DubboOrderServiceStarter.java">DubboOrderServiceStarter&lt;/a>).&lt;/li>
&lt;li>运行BusinessService入口 (&lt;a href="https://github.com/apache/dubbo-samples/blob/master/99-integration/dubbo-samples-transaction/src/main/java/org/apache/dubbo/samples/starter/DubboBusinessTester.java">DubboBusinessTester&lt;/a>).&lt;/li>
&lt;/ul>
&lt;h3 id="相关项目">相关项目&lt;/h3>
&lt;ul>
&lt;li>Seata: &lt;a href="https://github.com/seata/seata">https://github.com/seata/seata&lt;/a>&lt;/li>
&lt;li>Seata Samples : &lt;a href="https://github.com/seata/seata-samples">https://github.com/seata/seata-samples&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Blog: 新版 Dubbo Admin 介绍</title><link>https://dubbo.apache.org/zh-cn/blog/2019/01/07/%E6%96%B0%E7%89%88-dubbo-admin-%E4%BB%8B%E7%BB%8D/</link><pubDate>Mon, 07 Jan 2019 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2019/01/07/%E6%96%B0%E7%89%88-dubbo-admin-%E4%BB%8B%E7%BB%8D/</guid><description>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>github: https://github.com/apache/dubbo-ops
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Dubbo Admin之前的版本过于老旧,也长期疏于维护,因此在去年年中的时候,对该项目进行了一次重构,项目结构上的变化如下:&lt;/p>
&lt;ul>
&lt;li>将后端框架从webx替换成spring boot&lt;/li>
&lt;li>前端采用Vue和Vuetify.js作为开发框架&lt;/li>
&lt;li>移除velocity模板&lt;/li>
&lt;li>集成swagger,提供api管理功能&lt;/li>
&lt;/ul>
&lt;p>当前版本的Dubbo Admin包含了之前版本中的绝大部分功能,包括服务治理,服务查询等,同时支持了Dubbo2.7中服务治理的新特性。&lt;/p>
&lt;h2 id="配置规范">配置规范&lt;/h2>
&lt;p>由于在Dubbo2.7中,配置中心和注册中心做了分离,并且增加了元数据中心,因此Dubbo Admin的配置方式也做了更新,&lt;code>application.properties&lt;/code>中的配置如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-properties" data-lang="properties">&lt;span style="display:flex;">&lt;span>admin.registry.address&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">zookeeper://127.0.0.1:2181&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>admin.config-center&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">zookeeper://127.0.0.1:2181&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>admin.metadata-report.address&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">zookeeper://127.0.0.1:2181&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>也可以和Dubbo2.7一样,在配置中心指定元数据和注册中心的地址,以zookeeper为例,配置的路径和内容如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-properties" data-lang="properties">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># /dubbo/config/dubbo/dubbo.properties&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.registry.address&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">zookeeper://127.0.0.1:2181&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.metadata-report.address&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">zookeeper://127.0.0.1:2181&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>配置中心里的地址会覆盖掉本地&lt;code>application.properties&lt;/code>的配置&lt;/p>
&lt;h2 id="功能介绍">功能介绍&lt;/h2>
&lt;p>功能上,主要延续了之前版本的功能,包括服务查询和服务治理,2.7版本在服务治理的功能上有了很大的改进,这些改进也大部分都会以Dubbo Admin作为入口来体现。&lt;/p>
&lt;h3 id="标签路由">标签路由&lt;/h3>
&lt;p>标签路由是Dubbo2.7引入的新功能,配置以应用作为维度,给不同的服务器打上不同名字的标签,配置如下图所示:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/admin/route.jpg" alt="tag">&lt;/p>
&lt;p>调用的时候,客户端可以通过&lt;code>setAttachment&lt;/code>的方式,来设置不同的标签名称,比如本例中,&lt;code>setAttachment(tag1)&lt;/code>,客户端的选址范围就在如图所示的三台机器中,可以通过这种方式来实现流量隔离,灰度发布等功能。&lt;/p>
&lt;h3 id="应用级别的服务治理">应用级别的服务治理&lt;/h3>
&lt;p>在Dubbo2.6及更早版本中,所有的服务治理规则都只针对服务粒度,如果要把某条规则作用到应用粒度上,需要为应用下的所有服务配合相同的规则,变更,删除的时候也需要对应的操作,这样的操作很不友好,因此Dubbo2.7版本中增加了应用粒度的服务治理操作,对于条件路由(包括黑白名单),动态配置(包括权重,负载均衡)都可以做应用级别的配置:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/admin/conditionRoute.jpg" alt="condition">&lt;/p>
&lt;p>上图是条件路由的配置,可以按照应用名,服务名两个维度来填写,也可以按照这两个维度来查询。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/admin/weight.jpg" alt="weight">&lt;/p>
&lt;p>条件路由,标签路由和动态配置都采用了&lt;code>yaml&lt;/code>格式的文本编写,其他的规则配置还是采用了表单的形式。&lt;/p>
&lt;h4 id="关于兼容性">关于兼容性&lt;/h4>
&lt;p>Dubbo2.6到Dubbo2.7,服务治理发生了比较大的变化,Dubbo Admin兼容两个版本的用法:&lt;/p>
&lt;ul>
&lt;li>对于服务级别的配置,会按照Dubbo2.6(URL)和Dubbo2.7(配置文件)两种格式进行写入,保证Dubbo2.6的客户端能够正确读取,解析规则&lt;/li>
&lt;li>对于应用级别的配置,包括标签路由,只会按照Dubbo2.7的格式进行写入,因为Dubbo2.6无此功能,不需要做向前兼容。&lt;/li>
&lt;li>Dubbo Admin只会按照Dubbo2.7的格式进行配置读取,因此,所有在Dubbo Admin上做的配置都可以被读到,但是之前遗留的,Dubbo2.6格式的URL无法被读取。&lt;/li>
&lt;li>对于同一个应用或者服务,每种规则只能够配置一条,否则新的会覆盖旧的。&lt;/li>
&lt;/ul>
&lt;h3 id="配置管理">配置管理&lt;/h3>
&lt;p>配置管理也是配合Dubbo2.7新增的功能,在Dubbo2.7中,增加了全局和应用维度的配置,&lt;/p>
&lt;ul>
&lt;li>全局配置:&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/admin/config.jpg" alt="config">&lt;/p>
&lt;p>全局配置里可以指定注册中心,元数据中心的地址,服务端和客户端的超时时间等,这些配置在全局内生效。除了配置写入,也可以用来查看。如果使用zookeeper作为注册中心和元数据中心,还可以看到配置文件所在位置的目录结构。&lt;/p>
&lt;ul>
&lt;li>应用, 服务配置&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/admin/appConfig.jpg" alt="appConfig">&lt;/p>
&lt;p>应用级别的配置可以为应用或者应用内的服务指定配置,在服务维度上,需要区分提供者和消费者。&lt;code>dubbo.reference.{serviceName}&lt;/code>表示作为该服务消费者的配置,&lt;code>dubbo.provider.{servcieName}&lt;/code>表示作为该服务提供者的配置。优先级服务 &amp;gt; 应用 &amp;gt; 全局。其中注册中心和元数据中心的地址,只能在全局配置中指定,这也是Dubbo2.7中推荐的使用方式。&lt;/p>
&lt;h3 id="元数据和服务测试">元数据和服务测试&lt;/h3>
&lt;p>元数据是Dubbo2.7中新引入的元素,主要的使用场景就在Dubbo Admin中,主要体现在两个地方:&lt;/p>
&lt;ul>
&lt;li>服务详情展示:&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/admin/metadata.jpg" alt="metadata">&lt;/p>
&lt;p>跟之前版本相比,Dubbo2.7中增加了对服务方法完整签名的记录,因此服务详情中也增加了方法信息的详情,可以看到方法名,方法参数列表以及返回值信息。&lt;/p>
&lt;ul>
&lt;li>服务测试:&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/admin/test.jpg" alt="test">&lt;/p>
&lt;p>更重要的,元数据为服务测试提供了数据基础,可以在页面上调用真实的服务提供者,方便测试,也不需要为了调用服务去搭建一套Dubbo环境以及编写消费端代码。&lt;/p></description></item><item><title>Blog: Dubbo 融合 Nacos 成为注册中心</title><link>https://dubbo.apache.org/zh-cn/blog/2018/11/07/dubbo-%E8%9E%8D%E5%90%88-nacos-%E6%88%90%E4%B8%BA%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83/</link><pubDate>Wed, 07 Nov 2018 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2018/11/07/dubbo-%E8%9E%8D%E5%90%88-nacos-%E6%88%90%E4%B8%BA%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83/</guid><description>
&lt;p>Nacos 作为 Dubbo 生态系统中重要的注册中心实现,其中 &lt;a href="https://github.com/apache/dubbo/tree/master/dubbo-registry/dubbo-registry-nacos">&lt;code>dubbo-registry-nacos&lt;/code>&lt;/a> 则是 Dubbo 融合 Nacos 注册中心的实现。&lt;/p>
&lt;h2 id="预备工作">预备工作&lt;/h2>
&lt;p>当您将 &lt;a href="https://github.com/apache/dubbo/tree/master/dubbo-registry/dubbo-registry-nacos">&lt;code>dubbo-registry-nacos&lt;/code>&lt;/a> 整合到您的 Dubbo 工程之前,请确保后台已经启动 Nacos 服务。如果您尚且不熟悉 Nacos 的基本使用的话,可先行参考 &lt;a href="https://nacos.io/en-us/docs/quick-start.html">Nacos 快速入门&lt;/a>:https://nacos.io/en-us/docs/quick-start.html。建议使用 Nacos &lt;code>0.6.1&lt;/code> 以上的版本。&lt;/p>
&lt;h2 id="快速上手">快速上手&lt;/h2>
&lt;p>Dubbo 融合 Nacos 成为注册中心的操作步骤非常简单,大致步骤可分为“增加 Maven 依赖”以及“配置注册中心“。&lt;/p>
&lt;h3 id="增加-maven-依赖">增加 Maven 依赖&lt;/h3>
&lt;p>首先,您需要 &lt;code>dubbo-registry-nacos&lt;/code> 的 Maven 依赖添加到您的项目中 &lt;code>pom.xml&lt;/code> 文件中,并且强烈地推荐您使用 Dubbo &lt;code>2.6.5&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dependencies&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">&amp;lt;!-- Dubbo Nacos registry dependency --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>com.alibaba&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>dubbo-registry-nacos&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;version&amp;gt;&lt;/span>0.0.2&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">&amp;lt;!-- Keep latest Nacos client version --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>com.alibaba.nacos&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>nacos-client&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;version&amp;gt;&lt;/span>[0.6.1,)&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">&amp;lt;!-- Dubbo dependency --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>com.alibaba&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>dubbo&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;version&amp;gt;&lt;/span>2.6.5&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">&amp;lt;!-- Alibaba Spring Context extension --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>com.alibaba.spring&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>spring-context-support&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;version&amp;gt;&lt;/span>1.0.2&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/dependencies&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>当项目中添加 &lt;code>dubbo-registry-nacos&lt;/code> 后,您无需显示地编程实现服务发现和注册逻辑,实际实现由该三方包提供,接下来配置 Naocs 注册中心。&lt;/p>
&lt;h3 id="配置注册中心">配置注册中心&lt;/h3>
&lt;p>假设您 Dubbo 应用使用 Spring Framework 装配,将有两种配置方法可选,分别为:&lt;a href="https://mercyblitz.github.io/2018/01/18/Dubbo-%E5%A4%96%E9%83%A8%E5%8C%96%E9%85%8D%E7%BD%AE/">Dubbo Spring 外部化配置&lt;/a>以及 Spring XML 配置文件以及 ,笔者强烈推荐前者。&lt;/p>
&lt;h3 id="dubbo-spring-外部化配置httpsmercyblitzgithubio20180118dubbo-e5a496e983a8e58c96e9858de7bdae">&lt;a href="https://mercyblitz.github.io/2018/01/18/Dubbo-%E5%A4%96%E9%83%A8%E5%8C%96%E9%85%8D%E7%BD%AE/">Dubbo Spring 外部化配置&lt;/a>&lt;/h3>
&lt;p>Dubbo Spring 外部化配置是由 Dubbo &lt;code>2.5.8&lt;/code> 引入的新特性,可通过 Spring &lt;code>Environment&lt;/code> 属性自动地生成并绑定 Dubbo 配置 Bean,实现配置简化,并且降低微服务开发门槛。&lt;/p>
&lt;p>假设您 Dubbo 应用的使用 Zookeeper 作为注册中心,并且其服务器 IP 地址为:&lt;code>10.20.153.10&lt;/code>,同时,该注册地址作为 Dubbo 外部化配置属性存储在 &lt;code>dubbo-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">## application&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">your-dubbo-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">## Zookeeper registry address&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://10.20.153.10:2181&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>假设您的 Nacos Server 同样运行在服务器 &lt;code>10.20.153.10&lt;/code> 上,并使用默认 Nacos 服务端口 &lt;code>8848&lt;/code>,您只需将 &lt;code>dubbo.registry.address&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">## 其他属性保持不变&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">## Nacos registry address&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">nacos://10.20.153.10:8848&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>...
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>随后,重启您的 Dubbo 应用,Dubbo 的服务提供和消费信息在 Nacos 控制台中可以显示:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/dubbo-registry-nacos-1.png" alt="image-20181213103845976">&lt;/p>
&lt;p>如图所示,服务名前缀为 &lt;code>providers:&lt;/code> 的信息为服务提供者的元信息,&lt;code>consumers:&lt;/code> 则代表服务消费者的元信息。点击“&lt;strong>详情&lt;/strong>”可查看服务状态详情:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/dubbo-registry-nacos-2.png" alt="image-20181213104145998">&lt;/p>
&lt;p>如果您正在使用 Spring XML 配置文件装配 Dubbo 注册中心的话,请参考下一节。&lt;/p>
&lt;h3 id="spring-xml-配置文件">Spring XML 配置文件&lt;/h3>
&lt;p>同样,假设您 Dubbo 应用的使用 Zookeeper 作为注册中心,并且其服务器 IP 地址为:&lt;code>10.20.153.10&lt;/code>,并且装配 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-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 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;&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-provider-xml-demo&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;!-- 使用 Zookeeper 注册中心 --&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://10.20.153.10: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;/beans&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>与 &lt;a href="https://mercyblitz.github.io/2018/01/18/Dubbo-%E5%A4%96%E9%83%A8%E5%8C%96%E9%85%8D%E7%BD%AE/">Dubbo Spring 外部化配置&lt;/a> 配置类似,只需要调整 &lt;code>address&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:#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 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;&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-provider-xml-demo&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;!-- 使用 Nacos 注册中心 --&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;nacos://10.20.153.10:8848&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>重启 Dubbo 应用后,您同样也能发现服务提供方和消费方的注册元信息呈现在 Nacos 控制台中:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/dubbo-registry-nacos-3.png" alt="image-20181213113049185">&lt;/p>
&lt;p>您是否绝对配置或切换 Nacos 注册中心超级 Easy 呢?如果您仍旧意犹未尽或者不甚明白的话,可参考以下完整的示例。&lt;/p>
&lt;h2 id="完整示例">完整示例&lt;/h2>
&lt;p>以上图片中的元数据源于 Dubbo Spring 注解驱动示例以及 Dubbo Spring XML 配置驱动示例,下面将分别介绍两者,您可以选择自己偏好的编程模型。在正式讨论之前,先来介绍两者的预备工作,因为它们皆依赖 Java 服务接口和实现。同时,&lt;strong>请确保本地(&lt;code>127.0.0.1&lt;/code>)环境已启动 Nacos 服务&lt;/strong>。&lt;/p>
&lt;h3 id="示例接口与实现">示例接口与实现&lt;/h3>
&lt;p>首先定义示例接口,如下所示:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> com.alibaba.dubbo.demo.service&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/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"> * 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"> * @since 2.6.5
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#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">sayName&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;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.service&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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.rpc.RpcContext&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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.springframework.beans.factory.annotation.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>&lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Default {@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"> * @since 2.6.5
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Service&lt;/span>&lt;span style="color:#719e07">(&lt;/span>version &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;${demo.service.version}&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">DefaultService&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">@Value&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;${demo.service.name}&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> String serviceName&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/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">sayName&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> RpcContext rpcContext &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:#719e07">return&lt;/span> String&lt;span style="color:#719e07">.&lt;/span>format&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Service [name :%s , port : %d] %s(\&amp;#34;%s\&amp;#34;) : Hello,%s&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceName&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> rpcContext&lt;span style="color:#719e07">.&lt;/span>getLocalPort&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>getMethodName&lt;span style="color:#719e07">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&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>接口与实现准备妥当后,下面将采用注解驱动和 XML 配置驱动各自实现。&lt;/p>
&lt;h3 id="spring-注解驱动示例">Spring 注解驱动示例&lt;/h3>
&lt;p>Dubbo &lt;code>2.5.7&lt;/code> 重构了 Spring 注解驱动的编程模型。&lt;/p>
&lt;h4 id="服务提供方注解驱动实现">服务提供方注解驱动实现&lt;/h4>
&lt;ul>
&lt;li>定义 Dubbo 提供方外部化配置属性源 - &lt;code>provider-config.properties&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-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.name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">dubbo-provider-demo&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">## Nacos registry address&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">nacos://127.0.0.1:8848&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">## Dubbo 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">-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"># Provider @Service version&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>demo.service.version&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">1.0.0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>demo.service.name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">demoService&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>实现服务提供方引导类 - &lt;code>DemoServiceProviderBootstrap&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:#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.spring.context.annotation.EnableDubbo&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.service.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:#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 style="color:#719e07">import&lt;/span> org.springframework.context.annotation.PropertySource&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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.io.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:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * {@link DemoService} provider demo
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&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">@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.demo.service&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>value &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;classpath:/provider-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">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DemoServiceProviderBootstrap&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/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> 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>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> context&lt;span style="color:#719e07">.&lt;/span>register&lt;span style="color:#719e07">(&lt;/span>DemoServiceProviderBootstrap&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> 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;DemoService provider is starting...&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;span 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> 激活 Dubbo 注解驱动以及外部化配置,其 &lt;code>scanBasePackages&lt;/code> 属性扫描指定 Java 包,将所有标注 &lt;code>@Service&lt;/code> 的服务接口实现类暴露为 Spring Bean,随即被导出 Dubbo 服务。&lt;/p>
&lt;p>&lt;code>@PropertySource&lt;/code> 是 Spring Framework 3.1 引入的标准导入属性配置资源注解,它将为 Dubbo 提供外部化配置。&lt;/p>
&lt;h4 id="服务消费方注解驱动实现">服务消费方注解驱动实现&lt;/h4>
&lt;ul>
&lt;li>定义 Dubbo 消费方外部化配置属性源 - &lt;code>consumer-config.properties&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-properties" data-lang="properties">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">## Dubbo Application info&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-consumer-demo&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">## Nacos registry address&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">nacos://127.0.0.1:8848&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># @Reference version&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>demo.service.version&lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">1.0.0&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>同样地,&lt;code>dubbo.registry.address&lt;/code> 属性指向 Nacos 注册中心,其他 Dubbo 服务相关的元信息通过 Nacos 注册中心获取。&lt;/p>
&lt;ul>
&lt;li>实现服务消费方引导类 - &lt;code>DemoServiceConsumerBootstrap&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:#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.config.spring.context.annotation.EnableDubbo&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.service.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:#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 style="color:#719e07">import&lt;/span> org.springframework.context.annotation.PropertySource&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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> javax.annotation.PostConstruct&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> java.io.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:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * {@link DemoService} consumer demo
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&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">@EnableDubbo&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>value &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;classpath:/consumer-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">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DemoServiceConsumerBootstrap&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/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>version &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;${demo.service.version}&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">@PostConstruct&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">init&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">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> 0&lt;span style="color:#719e07">;&lt;/span> i &lt;span style="color:#719e07">&amp;lt;&lt;/span> 10&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> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>demoService&lt;span style="color:#719e07">.&lt;/span>sayName&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;小马哥(mercyblitz)&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>&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>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> context&lt;span style="color:#719e07">.&lt;/span>register&lt;span style="color:#719e07">(&lt;/span>DemoServiceConsumerBootstrap&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> context&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;/code>&lt;/pre>&lt;/div>&lt;p>同样地,&lt;code>@EnableDubbo&lt;/code> 注解激活 Dubbo 注解驱动和外部化配置,不过当前属于服务消费者,无需指定 Java 包名扫描标注 &lt;code>@Service&lt;/code> 的服务实现。&lt;/p>
&lt;p>&lt;code>@Reference&lt;/code> 是 Dubbo 远程服务的依赖注入注解,需要服务提供方和消费端约定接口(interface)、版本(version)以及分组(group)信息。在当前服务消费示例中,&lt;code>DemoService&lt;/code> 的服务版本来源于属性配置文件 &lt;code>consumer-config.properties&lt;/code>。&lt;/p>
&lt;p>&lt;code>@PostConstruct&lt;/code> 部分代码则说明当 &lt;code>DemoServiceConsumerBootstrap&lt;/code> Bean 初始化时,执行十次 Dubbo 远程方法调用。&lt;/p>
&lt;h4 id="运行注解驱动示例">运行注解驱动示例&lt;/h4>
&lt;p>在本地启动两次 &lt;code>DemoServiceProviderBootstrap&lt;/code>,注册中心将出现两个健康服务:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/dubbo-registry-nacos-4.png" alt="image-20181213123909636">&lt;/p>
&lt;p>再运行 &lt;code>DemoServiceConsumerBootstrap&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>Service [name :demoService , port : 20880] sayName(&amp;#34;小马哥(mercyblitz)&amp;#34;) : Hello,小马哥(mercyblitz)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Service [name :demoService , port : 20881] sayName(&amp;#34;小马哥(mercyblitz)&amp;#34;) : Hello,小马哥(mercyblitz)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Service [name :demoService , port : 20880] sayName(&amp;#34;小马哥(mercyblitz)&amp;#34;) : Hello,小马哥(mercyblitz)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Service [name :demoService , port : 20880] sayName(&amp;#34;小马哥(mercyblitz)&amp;#34;) : Hello,小马哥(mercyblitz)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Service [name :demoService , port : 20881] sayName(&amp;#34;小马哥(mercyblitz)&amp;#34;) : Hello,小马哥(mercyblitz)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Service [name :demoService , port : 20881] sayName(&amp;#34;小马哥(mercyblitz)&amp;#34;) : Hello,小马哥(mercyblitz)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Service [name :demoService , port : 20880] sayName(&amp;#34;小马哥(mercyblitz)&amp;#34;) : Hello,小马哥(mercyblitz)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Service [name :demoService , port : 20880] sayName(&amp;#34;小马哥(mercyblitz)&amp;#34;) : Hello,小马哥(mercyblitz)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Service [name :demoService , port : 20881] sayName(&amp;#34;小马哥(mercyblitz)&amp;#34;) : Hello,小马哥(mercyblitz)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Service [name :demoService , port : 20881] sayName(&amp;#34;小马哥(mercyblitz)&amp;#34;) : Hello,小马哥(mercyblitz)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>运行无误,并且服务消费方使用了负载均衡策略,将十次 RPC 调用平均分摊到两个 Dubbo 服务提供方实例中。&lt;/p>
&lt;h3 id="spring-xml-配置驱动示例">Spring XML 配置驱动示例&lt;/h3>
&lt;p>Spring XML 配置驱动是传统 Spring 装配组件的编程模型。&lt;/p>
&lt;h4 id="服务提供方--xml-配置驱动">服务提供方 XML 配置驱动&lt;/h4>
&lt;ul>
&lt;li>定义服务提供方 XML 上下文配置文件 - &lt;code>/META-INF/spring/dubbo-provider-context.xml&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-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 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;&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-provider-xml-demo&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;!-- 使用 Nacos 注册中心 --&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;nacos://127.0.0.1:8848&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;!-- 用dubbo协议在随机端口暴露服务 --&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;-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>&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> interface=&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.demo.service.DemoService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;demoService&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;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">&amp;lt;!-- 和本地bean一样实现服务 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;bean&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span> class=&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.demo.service.DefaultService&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;ul>
&lt;li>实现服务提供方引导类 - &lt;code>DemoServiceProviderXmlBootstrap&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-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>package com.alibaba.dubbo.demo.provider;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>import com.alibaba.dubbo.demo.service.DemoService;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>import org.springframework.context.support.ClassPathXmlApplicationContext;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>import java.io.IOException;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>/**
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> * {@link DemoService} provider demo XML bootstrap
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> */
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>public class DemoServiceProviderXmlBootstrap {
&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) throws IOException {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> context.setConfigLocation(&amp;#34;/META-INF/spring/dubbo-provider-context.xml&amp;#34;);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> context.refresh();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(&amp;#34;DemoService provider (XML) is starting...&amp;#34;);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.in.read();
&lt;/span>&lt;/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="服务消费方-xml-配置驱动">服务消费方 XML 配置驱动&lt;/h4>
&lt;ul>
&lt;li>定义服务消费方 XML 上下文配置文件 - &lt;code>/META-INF/spring/dubbo-consumer-context.xml&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-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 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;&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-consumer-xml-demo&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;!-- 使用 Nacos 注册中心 --&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;nacos://127.0.0.1:8848&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:reference&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.demo.service.DemoService&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;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;ul>
&lt;li>实现服务消费方引导类 - &lt;code>DemoServiceConsumerXmlBootstrap&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:#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.demo.service.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:#719e07">import&lt;/span> org.springframework.context.support.ClassPathXmlApplicationContext&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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.io.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:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * {@link DemoService} consumer demo XML bootstrap
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&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">DemoServiceConsumerXmlBootstrap&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/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> 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>&lt;/span>&lt;span style="display:flex;">&lt;span> context&lt;span style="color:#719e07">.&lt;/span>setConfigLocation&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;/META-INF/spring/dubbo-consumer-context.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>refresh&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;DemoService consumer (XML) is starting...&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&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>&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span>&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:#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> 0&lt;span style="color:#719e07">;&lt;/span> i &lt;span style="color:#719e07">&amp;lt;&lt;/span> 10&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> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>demoService&lt;span style="color:#719e07">.&lt;/span>sayName&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;小马哥(mercyblitz)&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> context&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;/code>&lt;/pre>&lt;/div>&lt;h4 id="运行-xml-配置驱动示例">运行 XML 配置驱动示例&lt;/h4>
&lt;p>同样地,先启动两个 &lt;code>DemoServiceProviderXmlBootstrap&lt;/code> 引导类,观察 Nacos 注册中心服务提供者变化:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/dubbo-registry-nacos-5.png" alt="image-20181213125527201">&lt;/p>
&lt;p>XML 配置驱动的服务版本为 &lt;code>2.0.0&lt;/code>,因此注册服务无误。&lt;/p>
&lt;p>再运行服务消费者引导类 &lt;code>DemoServiceConsumerXmlBootstrap&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>Service [name :null , port : 20882] sayName(&amp;#34;小马哥(mercyblitz)&amp;#34;) : Hello,小马哥(mercyblitz)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Service [name :null , port : 20882] sayName(&amp;#34;小马哥(mercyblitz)&amp;#34;) : Hello,小马哥(mercyblitz)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Service [name :null , port : 20883] sayName(&amp;#34;小马哥(mercyblitz)&amp;#34;) : Hello,小马哥(mercyblitz)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Service [name :null , port : 20882] sayName(&amp;#34;小马哥(mercyblitz)&amp;#34;) : Hello,小马哥(mercyblitz)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Service [name :null , port : 20882] sayName(&amp;#34;小马哥(mercyblitz)&amp;#34;) : Hello,小马哥(mercyblitz)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Service [name :null , port : 20883] sayName(&amp;#34;小马哥(mercyblitz)&amp;#34;) : Hello,小马哥(mercyblitz)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Service [name :null , port : 20882] sayName(&amp;#34;小马哥(mercyblitz)&amp;#34;) : Hello,小马哥(mercyblitz)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Service [name :null , port : 20883] sayName(&amp;#34;小马哥(mercyblitz)&amp;#34;) : Hello,小马哥(mercyblitz)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Service [name :null , port : 20883] sayName(&amp;#34;小马哥(mercyblitz)&amp;#34;) : Hello,小马哥(mercyblitz)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Service [name :null , port : 20883] sayName(&amp;#34;小马哥(mercyblitz)&amp;#34;) : Hello,小马哥(mercyblitz)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>结果同样运行和负载均衡正常,不过由于当前示例尚未添加属性 &lt;code>demo.service.name&lt;/code> 的缘故,因此,“name”部分信息输出为 &lt;code>null&lt;/code>。更多内容请参考:https://github.com/apache/dubbo/tree/master/dubbo-registry/dubbo-registry-nacos。&lt;/p>
&lt;p>如果您关注或喜爱 Dubbo 以及 Nacos 等开源工程,不妨为它们点 “star”,加油打气链接:&lt;/p>
&lt;ul>
&lt;li>Apache Dubbo:https://github.com/apache/dubbo&lt;/li>
&lt;li>Dubbo Nacos Registry:https://github.com/apache/dubbo/tree/master/dubbo-registry/dubbo-registry-nacos&lt;/li>
&lt;li>Alibaba Nacos:https://github.com/alibaba/nacos&lt;/li>
&lt;/ul></description></item><item><title>Blog: Spring应用快速集成Dubbo + Hystrix</title><link>https://dubbo.apache.org/zh-cn/blog/2018/08/22/spring%E5%BA%94%E7%94%A8%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90dubbo--hystrix/</link><pubDate>Wed, 22 Aug 2018 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2018/08/22/spring%E5%BA%94%E7%94%A8%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90dubbo--hystrix/</guid><description>
&lt;h2 id="背景">背景&lt;/h2>
&lt;p>Hystrix 旨在通过控制那些访问远程系统、服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。Hystrix具备拥有回退机制和断路器功能的线程和信号隔离,请求缓存和请求打包,以及监控和配置等功能。&lt;/p>
&lt;p>Dubbo是Alibaba开源的,目前国内最流行的java rpc框架。&lt;/p>
&lt;p>本文介绍在spring应用里,怎么把Dubbo和Hystrix结合起来使用。&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/Netflix/Hystrix">https://github.com/Netflix/Hystrix&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/apache/dubbo">https://github.com/apache/dubbo&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="spring-boot应用">Spring Boot应用&lt;/h2>
&lt;p>Demo地址: &lt;a href="https://github.com/dubbo/dubbo-samples/tree/master/4-governance/dubbo-samples-spring-boot-hystrix">https://github.com/dubbo/dubbo-samples/tree/master/4-governance/dubbo-samples-spring-boot-hystrix&lt;/a>&lt;/p>
&lt;h3 id="生成dubbo集成spring-boot的应用">生成dubbo集成spring boot的应用&lt;/h3>
&lt;p>对于不熟悉dubbo 集成spring boot应用的同学,可以在这里直接生成dubbo + spring boot的工程: &lt;a href="http://start.dubbo.io/">http://start.dubbo.io/&lt;/a>&lt;/p>
&lt;h3 id="配置spring-cloud-starter-netflix-hystrix">配置spring-cloud-starter-netflix-hystrix&lt;/h3>
&lt;p>spring boot官方提供了对hystrix的集成,直接在pom.xml里加入依赖:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>org.springframework.cloud&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>spring-cloud-starter-netflix-hystrix&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;version&amp;gt;&lt;/span>1.4.4.RELEASE&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>然后在Application类上增加&lt;code>@EnableHystrix&lt;/code>来启用hystrix starter:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@SpringBootApplication&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@EnableHystrix&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ProviderApplication&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="配置provider端">配置Provider端&lt;/h3>
&lt;p>在Dubbo的Provider上增加&lt;code>@HystrixCommand&lt;/code>配置,这样子调用就会经过Hystrix代理。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Service&lt;/span>&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">HelloServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> HelloService &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@HystrixCommand&lt;/span>&lt;span style="color:#719e07">(&lt;/span>commandProperties &lt;span style="color:#719e07">=&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">@HystrixProperty&lt;/span>&lt;span style="color:#719e07">(&lt;/span>name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;circuitBreaker.requestVolumeThreshold&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> value &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;10&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">@HystrixProperty&lt;/span>&lt;span style="color:#719e07">(&lt;/span>name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;execution.isolation.thread.timeoutInMilliseconds&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> value &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;2000&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:#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:#586e75">// System.out.println(&amp;#34;async provider received: &amp;#34; + name);
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// return &amp;#34;annotation: hello, &amp;#34; + name;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RuntimeException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Exception to show hystrix enabled.&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="配置consumer端">配置Consumer端&lt;/h3>
&lt;p>对于Consumer端,则可以增加一层method调用,并在method上配置&lt;code>@HystrixCommand&lt;/code>。当调用出错时,会走到&lt;code>fallbackMethod = &amp;quot;reliable&amp;quot;&lt;/code>的调用里。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Reference&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">private&lt;/span> HelloService 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">@HystrixCommand&lt;/span>&lt;span style="color:#719e07">(&lt;/span>fallbackMethod &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;reliable&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> 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> 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:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">reliable&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;hystrix fallback 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;/code>&lt;/pre>&lt;/div>&lt;p>通过上面的配置,很简单地就完成了Spring Boot里Dubbo + Hystrix的集成。&lt;/p>
&lt;h2 id="传统spring-annotation应用">传统Spring Annotation应用&lt;/h2>
&lt;p>Demo地址: &lt;a href="https://github.com/dubbo/dubbo-samples/tree/master/4-governance/dubbo-samples-spring-hystrix">https://github.com/dubbo/dubbo-samples/tree/master/4-governance/dubbo-samples-spring-hystrix&lt;/a>&lt;/p>
&lt;p>传统spring annotation应用的配置其实也很简单,和spring boot应用不同的是:&lt;/p>
&lt;ol>
&lt;li>显式配置Spring AOP支持:&lt;code>@EnableAspectJAutoProxy&lt;/code>&lt;/li>
&lt;li>显式通过&lt;code>@Configuration&lt;/code>配置&lt;code>HystrixCommandAspect&lt;/code> Bean。&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">@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.annotation.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">@PropertySource&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;classpath:/spring/dubbo-consumer.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">@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.annotation.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">@EnableAspectJAutoProxy&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">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:#268bd2">@Bean&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> HystrixCommandAspect &lt;span style="color:#268bd2">hystrixCommandAspect&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span 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> HystrixCommandAspect&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;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="hystrix集成spring-aop原理">Hystrix集成Spring AOP原理&lt;/h2>
&lt;p>在上面的例子里可以看到,Hystrix对Spring的集成是通过Spring AOP来实现的。下面简单分析下实现。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Aspect&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">HystrixCommandAspect&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">@Pointcut&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand)&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">void&lt;/span> &lt;span style="color:#268bd2">hystrixCommandAnnotationPointcut&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&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">@Pointcut&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser)&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">void&lt;/span> &lt;span style="color:#268bd2">hystrixCollapserAnnotationPointcut&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="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">@Around&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;hystrixCommandAnnotationPointcut() || hystrixCollapserAnnotationPointcut()&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> Object &lt;span style="color:#268bd2">methodsAnnotatedWithHystrixCommand&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#268bd2">final&lt;/span> ProceedingJoinPoint joinPoint&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> Throwable &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Method method &lt;span style="color:#719e07">=&lt;/span> getMethodFromTarget&lt;span style="color:#719e07">(&lt;/span>joinPoint&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Validate&lt;span style="color:#719e07">.&lt;/span>notNull&lt;span style="color:#719e07">(&lt;/span>method&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;failed to get method from joinPoint: %s&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> joinPoint&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&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>isAnnotationPresent&lt;span style="color:#719e07">(&lt;/span>HystrixCommand&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> method&lt;span style="color:#719e07">.&lt;/span>isAnnotationPresent&lt;span style="color:#719e07">(&lt;/span>HystrixCollapser&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">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;method cannot be annotated with HystrixCommand and HystrixCollapser &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;annotations at the same time&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> MetaHolderFactory metaHolderFactory &lt;span style="color:#719e07">=&lt;/span> META_HOLDER_FACTORY_MAP&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>HystrixPointcutType&lt;span style="color:#719e07">.&lt;/span>of&lt;span style="color:#719e07">(&lt;/span>method&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MetaHolder metaHolder &lt;span style="color:#719e07">=&lt;/span> metaHolderFactory&lt;span style="color:#719e07">.&lt;/span>create&lt;span style="color:#719e07">(&lt;/span>joinPoint&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> HystrixInvokable invokable &lt;span style="color:#719e07">=&lt;/span> HystrixCommandFactory&lt;span style="color:#719e07">.&lt;/span>getInstance&lt;span style="color:#719e07">().&lt;/span>create&lt;span style="color:#719e07">(&lt;/span>metaHolder&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ExecutionType executionType &lt;span style="color:#719e07">=&lt;/span> metaHolder&lt;span style="color:#719e07">.&lt;/span>isCollapserAnnotationPresent&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">?&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metaHolder&lt;span style="color:#719e07">.&lt;/span>getCollapserExecutionType&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">:&lt;/span> metaHolder&lt;span style="color:#719e07">.&lt;/span>getExecutionType&lt;span style="color:#719e07">();&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>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>metaHolder&lt;span style="color:#719e07">.&lt;/span>isObservable&lt;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> CommandExecutor&lt;span style="color:#719e07">.&lt;/span>execute&lt;span style="color:#719e07">(&lt;/span>invokable&lt;span style="color:#719e07">,&lt;/span> executionType&lt;span style="color:#719e07">,&lt;/span> metaHolder&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> result &lt;span style="color:#719e07">=&lt;/span> executeObservable&lt;span style="color:#719e07">(&lt;/span>invokable&lt;span style="color:#719e07">,&lt;/span> executionType&lt;span style="color:#719e07">,&lt;/span> metaHolder&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="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>HystrixBadRequestException 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> e&lt;span style="color:#719e07">.&lt;/span>getCause&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> e&lt;span style="color:#719e07">.&lt;/span>getCause&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">:&lt;/span> e&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>HystrixRuntimeException 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> hystrixRuntimeExceptionToThrowable&lt;span style="color:#719e07">(&lt;/span>metaHolder&lt;span style="color:#719e07">,&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> 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;ol>
&lt;li>&lt;code>HystrixCommandAspect&lt;/code>里定义了两个注解的AspectJ Pointcut:&lt;code>@HystrixCommand&lt;/code>, &lt;code>@HystrixCollapser&lt;/code>。所有带这两个注解的spring bean都会经过AOP处理&lt;/li>
&lt;li>在&lt;code>@Around&lt;/code> AOP处理函数里,可以看到Hystrix会创建出&lt;code>HystrixInvokable&lt;/code>,再通过&lt;code>CommandExecutor&lt;/code>来执行&lt;/li>
&lt;/ol>
&lt;h2 id="spring-cloud-starter-netflix-hystrix的代码分析">spring-cloud-starter-netflix-hystrix的代码分析&lt;/h2>
&lt;ol>
&lt;li>
&lt;p>&lt;code>@EnableHystrix&lt;/code> 引入了&lt;code>@EnableCircuitBreaker&lt;/code>,&lt;code>@EnableCircuitBreaker&lt;/code>引入了&lt;code>EnableCircuitBreakerImportSelector&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">@EnableCircuitBreaker&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> EnableHystrix &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="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">@Import&lt;/span>&lt;span style="color:#719e07">(&lt;/span>EnableCircuitBreakerImportSelector&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:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">@interface&lt;/span> EnableCircuitBreaker &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;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;code>EnableCircuitBreakerImportSelector&lt;/code>继承了&lt;code>SpringFactoryImportSelector&amp;lt;EnableCircuitBreaker&amp;gt;&lt;/code>,使spring加载&lt;code>META-INF/spring.factories&lt;/code>里的&lt;code>EnableCircuitBreaker&lt;/code>声明的配置&lt;/p>
&lt;p>在&lt;code>META-INF/spring.factories&lt;/code>里可以找到下面的配置,也就是引入了&lt;code>HystrixCircuitBreakerConfiguration&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>org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerConfiguration&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>在&lt;code>HystrixCircuitBreakerConfiguration&lt;/code>里可以发现创建了&lt;code>HystrixCommandAspect&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">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">HystrixCircuitBreakerConfiguration&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/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> HystrixCommandAspect &lt;span style="color:#268bd2">hystrixCommandAspect&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span 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> HystrixCommandAspect&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;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;code>spring-cloud-starter-netflix-hystrix&lt;/code>实际上也是创建了&lt;code>HystrixCommandAspect&lt;/code>来集成Hystrix。&lt;/p>
&lt;p>另外&lt;code>spring-cloud-starter-netflix-hystrix&lt;/code>里还有metrics, health, dashboard等集成。&lt;/p>
&lt;h2 id="总结">总结&lt;/h2>
&lt;ul>
&lt;li>对于dubbo provider的&lt;code>@Service&lt;/code>是一个spring bean,直接在上面配置&lt;code>@HystrixCommand&lt;/code>即可&lt;/li>
&lt;li>对于dubbo consumer的&lt;code>@Reference&lt;/code>,可以通过加一层简单的spring method包装,配置&lt;code>@HystrixCommand&lt;/code>即可&lt;/li>
&lt;li>Hystrix本身提供&lt;code>HystrixCommandAspect&lt;/code>来集成Spring AOP,配置了&lt;code>@HystrixCommand&lt;/code>和&lt;code>@HystrixCollapser&lt;/code>的spring method都会被Hystrix处理&lt;/li>
&lt;/ul>
&lt;h2 id="链接">链接&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://github.com/Netflix/Hystrix">https://github.com/Netflix/Hystrix&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/apache/dubbo">https://github.com/apache/dubbo&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://start.dubbo.io/">http://start.dubbo.io/&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://cloud.spring.io/spring-cloud-netflix/single/spring-cloud-netflix.html#_circuit_breaker_hystrix_clients">https://cloud.spring.io/spring-cloud-netflix/single/spring-cloud-netflix.html#_circuit_breaker_hystrix_clients&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Blog: 在 Dubbo 应用中使用 Zookeeper</title><link>https://dubbo.apache.org/zh-cn/blog/2018/08/07/%E5%9C%A8-dubbo-%E5%BA%94%E7%94%A8%E4%B8%AD%E4%BD%BF%E7%94%A8-zookeeper/</link><pubDate>Tue, 07 Aug 2018 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2018/08/07/%E5%9C%A8-dubbo-%E5%BA%94%E7%94%A8%E4%B8%AD%E4%BD%BF%E7%94%A8-zookeeper/</guid><description>
&lt;h2 id="zookeeper-介绍">Zookeeper 介绍&lt;/h2>
&lt;h3 id="基本概念">基本概念&lt;/h3>
&lt;p>在现代的分布式应用中,往往会出现节点和节点之间的协调问题,其中就包括了:选主、集群管理、分布式锁、分布式配置管理、统一命名服务、状态同步等诉求。&lt;a href="https://zookeeper.apache.org">Apache Zookeeper&lt;/a>,正如它的名字所暗示的那样,&lt;em>动物园管理员&lt;/em>,就是为了解决这些诉求的一个分布式协调服务框架。&lt;/p>
&lt;p>为了保证高可用,ZooKeeper 本身也可以部署成集群模式,称之为 &lt;em>ZooKeeper ensemble&lt;/em>。ZooKeeper 集群中始终确保其中的一台为 leader 的角色,并通过 &lt;em>ZAB (Zookeeper Atomic Broadcast Protocol) &lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>&lt;/em> 协议确保所有节点上的信息的一致。客户端可以访问集群中的任何一台进行读写操作,而不用担心数据出现不一致的现象。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/zk-emsemble.png" alt="Diagram shows client-server architecture of ZooKeeper">
&lt;em>Image Credit : ebook -Zookeeper-Distributed Process Coordination from O&amp;rsquo;Reilly&lt;/em>&lt;/p>
&lt;p>Zookeeper 中的数据存储方式与传统的 UNIX 文件系统相似,节点按照树状结构来组织,其中,节点被称之为 &lt;em>znodes (ZooKeeper data nodes)&lt;/em>&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/zk-tree.png" alt="zk-tree">
&lt;em>Image Credit : ebook -Zookeeper-Distributed Process Coordination from O&amp;rsquo;Reilly&lt;/em>&lt;/p>
&lt;h3 id="基本用法">基本用法&lt;/h3>
&lt;p>可以通过直接下载的方式 &lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup>安装并运行 Zookeeper ,在 Mac 上也可以通过 Homebrew &lt;sup id="fnref:3">&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref">3&lt;/a>&lt;/sup> &lt;code>brew install zookeeper&lt;/code> 来安装,考虑到通用性,本文采用 docker 的方式来运行 Zookeeper。如果没有安装 docker,请先准备好 docker 环境 &lt;sup id="fnref:4">&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref">4&lt;/a>&lt;/sup>。&lt;/p>
&lt;h4 id="1-启动-zookeeper">1. 启动 Zookeeper&lt;/h4>
&lt;p>执行命令将 Zookeeper 运行在 docker 容器中&lt;/p>
&lt;div class="highlight">&lt;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>docker run --rm --name zookeeper -p 2181:2181 zookeeper
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="2-进入-zookeeper-容器">2. 进入 Zookeeper 容器&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-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>docker &lt;span style="color:#b58900">exec&lt;/span> -it zookeeper bash
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在 &lt;code>bin&lt;/code> 目录下有启动 Zookeeper 的命令 &lt;code>zkServer&lt;/code> 以及管理控制台 &lt;code>zkCli&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>bash-4.4# ls -l bin
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>total &lt;span style="color:#2aa198">36&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-rwxr-xr-x &lt;span style="color:#2aa198">1&lt;/span> zookeepe zookeepe &lt;span style="color:#2aa198">232&lt;/span> Mar &lt;span style="color:#2aa198">27&lt;/span> 04:32 README.txt
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-rwxr-xr-x &lt;span style="color:#2aa198">1&lt;/span> zookeepe zookeepe &lt;span style="color:#2aa198">1937&lt;/span> Mar &lt;span style="color:#2aa198">27&lt;/span> 04:32 zkCleanup.sh
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-rwxr-xr-x &lt;span style="color:#2aa198">1&lt;/span> zookeepe zookeepe &lt;span style="color:#2aa198">1056&lt;/span> Mar &lt;span style="color:#2aa198">27&lt;/span> 04:32 zkCli.cmd
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-rwxr-xr-x &lt;span style="color:#2aa198">1&lt;/span> zookeepe zookeepe &lt;span style="color:#2aa198">1534&lt;/span> Mar &lt;span style="color:#2aa198">27&lt;/span> 04:32 zkCli.sh
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-rwxr-xr-x &lt;span style="color:#2aa198">1&lt;/span> zookeepe zookeepe &lt;span style="color:#2aa198">1759&lt;/span> Mar &lt;span style="color:#2aa198">27&lt;/span> 04:32 zkEnv.cmd
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-rwxr-xr-x &lt;span style="color:#2aa198">1&lt;/span> zookeepe zookeepe &lt;span style="color:#2aa198">2696&lt;/span> Mar &lt;span style="color:#2aa198">27&lt;/span> 04:32 zkEnv.sh
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-rwxr-xr-x &lt;span style="color:#2aa198">1&lt;/span> zookeepe zookeepe &lt;span style="color:#2aa198">1089&lt;/span> Mar &lt;span style="color:#2aa198">27&lt;/span> 04:32 zkServer.cmd
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-rwxr-xr-x &lt;span style="color:#2aa198">1&lt;/span> zookeepe zookeepe &lt;span style="color:#2aa198">6773&lt;/span> Mar &lt;span style="color:#2aa198">27&lt;/span> 04:32 zkServer.sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="3-通过-zkcli-进入-zookeeper-管理界面">3. 通过 zkCli 进入 Zookeeper 管理界面&lt;/h4>
&lt;p>由于是通过 Docker 启动,Zookeeper 进程已经启动,并通过 2181 端口对外提供服务。&lt;/p>
&lt;div class="highlight">&lt;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>bash-4.4# ps
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>PID USER TIME COMMAND
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">1&lt;/span> zookeepe 0:02 /usr/lib/jvm/java-1.8-openjdk/jre/bin/java -Dzookeeper.log.dir&lt;span style="color:#719e07">=&lt;/span>. -Dzookeeper.root
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">32&lt;/span> root 0:00 bash
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">42&lt;/span> root 0:00 ps
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>因此可以直接通过 &lt;code>zkCli&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-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>bash-4.4# bin/zkCli.sh -server 127.0.0.1:2181
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Connecting to 127.0.0.1:2181
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>WATCHER::
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>WatchedEvent state:SyncConnected type:None path:null
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>zk: 127.0.0.1:2181&lt;span style="color:#719e07">(&lt;/span>CONNECTED&lt;span style="color:#719e07">)&lt;/span> 0&lt;span style="color:#719e07">]&lt;/span> &lt;span style="color:#b58900">help&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ZooKeeper -server host:port cmd args
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> stat path &lt;span style="color:#719e07">[&lt;/span>watch&lt;span style="color:#719e07">]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b58900">set&lt;/span> path data &lt;span style="color:#719e07">[&lt;/span>version&lt;span style="color:#719e07">]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ls path &lt;span style="color:#719e07">[&lt;/span>watch&lt;span style="color:#719e07">]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> delquota &lt;span style="color:#719e07">[&lt;/span>-n|-b&lt;span style="color:#719e07">]&lt;/span> path
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ls2 path &lt;span style="color:#719e07">[&lt;/span>watch&lt;span style="color:#719e07">]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> setAcl path acl
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> setquota -n|-b val path
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b58900">history&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> redo cmdno
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> printwatches on|off
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> delete path &lt;span style="color:#719e07">[&lt;/span>version&lt;span style="color:#719e07">]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> sync path
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> listquota path
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> rmr path
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> get path &lt;span style="color:#719e07">[&lt;/span>watch&lt;span style="color:#719e07">]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> create &lt;span style="color:#719e07">[&lt;/span>-s&lt;span style="color:#719e07">]&lt;/span> &lt;span style="color:#719e07">[&lt;/span>-e&lt;span style="color:#719e07">]&lt;/span> path data acl
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> addauth scheme auth
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> quit
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> getAcl path
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> close
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> connect host:port
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="4-zkcli-上的一些基本操作">4. zkCli 上的一些基本操作&lt;/h4>
&lt;p>创建 &lt;code>/hello-zone&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>&lt;span style="color:#719e07">[&lt;/span>zk: 127.0.0.1:2181&lt;span style="color:#719e07">(&lt;/span>CONNECTED&lt;span style="color:#719e07">)&lt;/span> 19&lt;span style="color:#719e07">]&lt;/span> create /hello-zone &lt;span style="color:#2aa198">&amp;#39;world&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Created /hello-zone
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>列出 &lt;code>/&lt;/code> 下的子节点,确认 &lt;code>hello-zone&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>&lt;span style="color:#719e07">[&lt;/span>zk: 127.0.0.1:2181&lt;span style="color:#719e07">(&lt;/span>CONNECTED&lt;span style="color:#719e07">)&lt;/span> 20&lt;span style="color:#719e07">]&lt;/span> ls /
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>zookeeper, hello-zone&lt;span style="color:#719e07">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>列出 &lt;code>/hello-zone&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>&lt;span style="color:#719e07">[&lt;/span>zk: 127.0.0.1:2181&lt;span style="color:#719e07">(&lt;/span>CONNECTED&lt;span style="color:#719e07">)&lt;/span> 21&lt;span style="color:#719e07">]&lt;/span> ls /hello-zone
&lt;/span>&lt;/span>&lt;span 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>/hello-zone&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>&lt;span style="color:#719e07">[&lt;/span>zk: 127.0.0.1:2181&lt;span style="color:#719e07">(&lt;/span>CONNECTED&lt;span style="color:#719e07">)&lt;/span> 22&lt;span style="color:#719e07">]&lt;/span> get /hello-zone
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>world
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="在-dubbo-中使用-zookeeper">在 Dubbo 中使用 Zookeeper&lt;/h2>
&lt;p>Dubbo 使用 Zookeeper 用于服务的注册发现和配置管理,在 Zookeeper 中数据的组织由下图所示:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/dubbo-in-zk.jpg" alt="dubbo-in-zk">&lt;/p>
&lt;p>首先,所有 Dubbo 相关的数据都组织在 &lt;code>/dubbo&lt;/code> 的根节点下。&lt;/p>
&lt;p>二级目录是服务名,如 &lt;code>com.foo.BarService&lt;/code>。&lt;/p>
&lt;p>三级目录有两个子节点,分别是 &lt;code>providers&lt;/code> 和 &lt;code>consumers&lt;/code>,表示该服务的提供者和消费者。&lt;/p>
&lt;p>四级目录记录了与该服务相关的每一个应用实例的 URL 信息,在 &lt;code>providers&lt;/code> 下的表示该服务的所有提供者,而在 &lt;code>consumers&lt;/code> 下的表示该服务的所有消费者。举例说明,&lt;code>com.foo.BarService&lt;/code> 的服务提供者在启动时将自己的 URL 信息注册到 &lt;code>/dubbo/com.foo.BarService/providers&lt;/code> 下;同样的,服务消费者将自己的信息注册到相应的 &lt;code>consumers&lt;/code> 下,同时,服务消费者会订阅其所对应的 &lt;code>providers&lt;/code> 节点,以便能够感知到服务提供方地址列表的变化。&lt;/p>
&lt;h3 id="准备示例代码">准备示例代码&lt;/h3>
&lt;p>本文的代码可以在 &lt;a href="https://github.com/apache/dubbo-samples/tree/master/3-extensions/registry/dubbo-samples-zookeeper">https://github.com/apache/dubbo-samples/tree/master/3-extensions/registry/dubbo-samples-zookeeper&lt;/a> 中找到。&lt;/p>
&lt;h4 id="1-接口定义">1. 接口定义&lt;/h4>
&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;h4 id="2-服务端服务实现">2. 服务端:服务实现&lt;/h4>
&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;h4 id="3-服务端组装">3. 服务端:组装&lt;/h4>
&lt;p>定义 ProviderConfiguration 来组装 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">@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;/code>&lt;/pre>&lt;/div>&lt;p>dubbo-provider.properties 是在 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-properties" data-lang="properties">&lt;span style="display:flex;">&lt;span>dubbo.application.name&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">demo-provider&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://$DOCKER_HOST:2181&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;/code>&lt;/pre>&lt;/div>&lt;p>由于 Zookeeper 运行在 Docker 容器中,需要注意的是:&lt;/p>
&lt;ul>
&lt;li>本文假定 Dubbo 应用运行在宿主机上,也就是 Docker 容器外,需要将 Zookeeper 的地址替换成环境变量 &lt;em>${DOCKER_HOST}&lt;/em> 所指定的 IP 地址,相关信息请查阅 Docker 官方文档&lt;/li>
&lt;li>如果 Dubbo 应用也是 Docker 化的应用,只需要用 Zookeeper 的容器名,在本文中容器名是 &lt;strong>zookeeper&lt;/strong>&lt;/li>
&lt;li>当然,如果不用容器方式启动 Zookeeper,只需要简单的将这里的 &lt;em>$DOCKER_HOST&lt;/em> 换成 &lt;strong>localhost&lt;/strong> 即可&lt;/li>
&lt;/ul>
&lt;h4 id="4-服务端启动服务">4. 服务端:启动服务&lt;/h4>
&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> 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;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>03/08/18 10:50:33:033 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>demo-provider&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>12938&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>1533264631849, dubbo version: 2.6.2, current host: 192.168.99.1
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>通过 Zookeeper 管理终端观察服务提供方的注册信息:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>$ docker &lt;span style="color:#b58900">exec&lt;/span> -it zookeeper bash
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>bash-4.4# bin/zkCli.sh -server localhost:218
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Connecting to localhost:2181
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Welcome to ZooKeeper!
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>JLine support is enabled
&lt;/span>&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>zk: localhost:2181&lt;span style="color:#719e07">(&lt;/span>CONNECTED&lt;span style="color:#719e07">)&lt;/span> 0&lt;span style="color:#719e07">]&lt;/span> ls /dubbo/com.alibaba.dubbo.samples.api.GreetingService/providers
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>dubbo%3A%2F%2F192.168.99.1%3A20880%2Fcom.alibaba.dubbo.samples.api.GreetingService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26dubbo%3D2.6.2%26generic%3Dfalse%26interface%3Dcom.alibaba.dubbo.samples.api.GreetingService%26methods%3DsayHello%26pid%3D12938%26side%3Dprovider%26timestamp%3D1533264631849&lt;span style="color:#719e07">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可以看到刚刚启动的 Dubbo 的服务在 &lt;code>providers&lt;/code> 节点下注册了自己的 URL 地址:&lt;em>dubbo://192.168.99.1:20880/com.alibaba.dubbo.samples.api.GreetingService?anyhost=true&amp;amp;application=demo-provider&amp;amp;dubbo=2.6.2&amp;amp;generic=false&amp;amp;interface=com.alibaba.dubbo.samples.api.GreetingService&amp;amp;methods=sayHello&amp;amp;pid=12938&amp;amp;side=provider&amp;amp;timestamp=1533264631849&lt;/em>&lt;/p>
&lt;h4 id="5-客户端引用服务">5. 客户端:引用服务&lt;/h4>
&lt;p>通过 &lt;code>@Reference&lt;/code> 来在客户端声明服务的引用,运行时将会通过该引用发起全程调用,而服务的目标地址将会从 Zookeeper 的 &lt;code>provider&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;h4 id="6-客户端组装">6. 客户端:组装&lt;/h4>
&lt;p>定义 ConsumerConfiguration 来组装 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">@PropertySource&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;classpath:/spring/dubbo-consumer.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">@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;/code>&lt;/pre>&lt;/div>&lt;p>dubbo-consumer.properties 是在 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-properties" data-lang="properties">&lt;span style="display:flex;">&lt;span>dubbo.application.name&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">demo-consumer&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://$DOCKER_HOST:2181&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.consumer.timeout&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">3000&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>与 &lt;strong>3. 服务端:组装&lt;/strong> 相同,需要根据自己的运行环境来修改 &lt;em>dubbo.registry.address&lt;/em> 中定义的 &lt;em>$DOCKER_HOST&lt;/em>。请参阅步骤 3 的说明部分。&lt;/p>
&lt;h4 id="7-客户端发起远程调用">7. 客户端:发起远程调用&lt;/h4>
&lt;p>运行 &lt;code>main&lt;/code> 向已经启动的服务提供方发起一次远程调用。Dubbo 会先向 Zookeeper 订阅服务地址,然后从返回的地址列表中选取一个,向对端发起调用:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&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>&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:#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>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> 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>&lt;/span>&lt;span style="display:flex;">&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;zookeeper&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;result: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> hello&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;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:#719e07">[&lt;/span>03/08/18 01:42:31:031 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: consumer://192.168.99.1/com.alibaba.dubbo.samples.api.GreetingService?application&lt;span style="color:#719e07">=&lt;/span>demo-consumer&amp;amp;&lt;span style="color:#268bd2">category&lt;/span>&lt;span style="color:#719e07">=&lt;/span>consumers&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">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>82406&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>1533274951195, dubbo version: 2.6.2, current host: 192.168.99.1 &lt;span style="color:#586e75">#1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>03/08/18 01:42:31:031 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> Subscribe: consumer://192.168.99.1/com.alibaba.dubbo.samples.api.GreetingService?application&lt;span style="color:#719e07">=&lt;/span>demo-consumer&amp;amp;&lt;span style="color:#268bd2">category&lt;/span>&lt;span style="color:#719e07">=&lt;/span>providers,configurators,routers&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">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>82406&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>1533274951195, dubbo version: 2.6.2, current host: 192.168.99.1 &lt;span style="color:#586e75">#2&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: hello, zookeeper
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>说明:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Register&lt;/strong>: consumer://192.168.99.1/&amp;hellip;&amp;amp;&lt;strong>category=consumers&lt;/strong>&amp;amp;:消费者向 Zookeeper 注册自己的信息,并放在 &lt;code>consumers&lt;/code> 节点下&lt;/li>
&lt;li>&lt;strong>Subscribe&lt;/strong>: consumer://192.168.99.1/&amp;hellip;&amp;amp;&lt;strong>category=providers,configurators,routers&lt;/strong>&amp;amp;:消费者同时向 Zookeeper 订阅了 &lt;code>providers&lt;/code>、&lt;code>configurators&lt;/code>、&lt;code>routers&lt;/code> 节点,其中 &lt;code>configurations&lt;/code> 与 Dubbo 配置相关,&lt;code>routers&lt;/code> 与路由规则相关,值得注意的是 &lt;code>providers&lt;/code> 节点的订阅,当有新的服务提供方加入后,由于订阅的关系,新的地址列表会推送给订阅方,服务的消费者也因此动态感知到了地址列表的变化。&lt;/li>
&lt;/ol>
&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-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>$ docker &lt;span style="color:#b58900">exec&lt;/span> -it zookeeper bash
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>bash-4.4# bin/zkCli.sh -server localhost:218
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Connecting to localhost:2181
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Welcome to ZooKeeper!
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>JLine support is enabled
&lt;/span>&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>zk: localhost:2181&lt;span style="color:#719e07">(&lt;/span>CONNECTED&lt;span style="color:#719e07">)&lt;/span> 4&lt;span style="color:#719e07">]&lt;/span> ls /dubbo/com.alibaba.dubbo.samples.api.GreetingService/consumers
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>consumer%3A%2F%2F192.168.99.1%2Fcom.alibaba.dubbo.samples.api.GreetingService%3Fapplication%3Ddemo-consumer%26category%3Dconsumers%26check%3Dfalse%26default.timeout%3D3000%26dubbo%3D2.6.2%26interface%3Dcom.alibaba.dubbo.samples.api.GreetingService%26methods%3DsayHello%26pid%3D82406%26side%3Dconsumer%26timestamp%3D1533274951195&lt;span style="color:#719e07">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可以看到 Dubbo 的服务消费者在 &lt;code>consumers&lt;/code> 节点下注册了自己的 URL 地址:&lt;em>consumer://192.168.99.1/com.alibaba.dubbo.samples.api.GreetingService?application=demo-consumer&amp;amp;category=providers,configurators,routers&amp;amp;default.timeout=3000&amp;amp;dubbo=2.6.2&amp;amp;interface=com.alibaba.dubbo.samples.api.GreetingService&amp;amp;methods=sayHello&amp;amp;pid=82406&amp;amp;side=consumer&amp;amp;timestamp=1533274951195&lt;/em>&lt;/p>
&lt;h2 id="总结">总结&lt;/h2>
&lt;p>本文侧重介绍了如何在 Dubbo 应用中使用 Zookeeper 做为注册中心,当然,本文也提到了 Zookeeper 在 Dubbo 的应用场景下还承担了配置中心和服务治理的职责。本文中的 Zookeeper 是单节点、Standalone 的模式,在生产环境中为了高可用的诉求,往往会组件 Zookeeper 集群,也就是 &lt;em>Zookeeper ensemble&lt;/em> 模式。&lt;/p>
&lt;p>通过本文的学习,读者可以掌握到:&lt;/p>
&lt;ul>
&lt;li>Zookeeper 的基本概念和基本用法&lt;/li>
&lt;li>Zookeeper 在 Dubbo 应用中的作用&lt;/li>
&lt;li>通过实战了解 Zookeeper 与 Dubbo 的交互&lt;/li>
&lt;li>Dubbo 在 Zookeeper 中服务注册、消费信息的存储方式&lt;/li>
&lt;/ul>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>&lt;a href="https://www.ixiacom.com/company/blog/apache-zab">https://www.ixiacom.com/company/blog/apache-zab&lt;/a>—zookeeper-atomic-broadcast-protocol&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://www.apache.org/dyn/closer.cgi/zookeeper/">https://www.apache.org/dyn/closer.cgi/zookeeper/&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://brew.sh">https://brew.sh&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://www.docker.com/community-edition">https://www.docker.com/community-edition&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: Sentinel 为 Dubbo 服务保驾护航</title><link>https://dubbo.apache.org/zh-cn/blog/2018/07/27/sentinel-%E4%B8%BA-dubbo-%E6%9C%8D%E5%8A%A1%E4%BF%9D%E9%A9%BE%E6%8A%A4%E8%88%AA/</link><pubDate>Fri, 27 Jul 2018 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2018/07/27/sentinel-%E4%B8%BA-dubbo-%E6%9C%8D%E5%8A%A1%E4%BF%9D%E9%A9%BE%E6%8A%A4%E8%88%AA/</guid><description>
&lt;p>在复杂的生产环境下可能部署着成千上万的 Dubbo 服务实例,流量持续不断地进入,服务之间进行相互调用。但是分布式系统中可能会因流量激增、系统负载过高、网络延迟等一系列问题,导致某些服务不可用,如果不进行相应的控制可能导致级联故障,影响服务的可用性,因此如何对流量进行合理的控制,成为保障服务稳定性的关键。&lt;/p>
&lt;p>&lt;a href="https://github.com/alibaba/Sentinel">Sentinel&lt;/a> 是阿里中间件团队开源的,面向分布式服务架构的轻量级流量控制产品,主要以流量为切入点,从&lt;strong>流量控制&lt;/strong>、&lt;strong>熔断降级&lt;/strong>、&lt;strong>系统负载保护&lt;/strong>等多个维度来帮助用户保护服务的稳定性。本文将基于 Dubbo,看看 Sentinel 是如何进行流量控制的,并且提供 Dubbo 整合 Sentinel 的最佳实践。&lt;/p>
&lt;h2 id="快速接入-sentinel">快速接入 Sentinel&lt;/h2>
&lt;p>Sentinel 意为&lt;strong>哨兵&lt;/strong>,这个命名形象的诠释了 Sentinel 在分布式系统中的工作角色和重要性。以 Sentinel 在 Dubbo 生态系统中的作用为例,Dubbo 的核心模块包括注册中心、服务提供方、服务消费方(服务调用方)和监控四个模块。Sentinel 通过对服务提供方和服务消费方的限流来进一步提升服务的可用性。接下来我们看看 Sentinel 对服务提供方和服务消费方限流的技术实现方式。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/architecture.png" alt="Dubbo Arch">&lt;/p>
&lt;p>Sentinel 提供了与 Dubbo 适配的模块 – &lt;a href="https://github.com/dubbo/dubbo-sentinel-support">Sentinel Dubbo Adapter&lt;/a>,包括针对服务提供方的过滤器和服务消费方的过滤器(Filter)。使用时我们只需引入以下模块(以 Maven 为例):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>com.alibaba.csp&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>sentinel-dubbo-adapter&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;version&amp;gt;&lt;/span>x.y.z&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>引入此依赖后,Dubbo 的服务接口和方法(包括调用端和服务端)就会成为 Sentinel 中的资源,在配置了规则后就可以自动享受到 Sentinel 的防护能力。同时提供了灵活的配置选项,例如若不希望开启 Sentinel Dubbo Adapter 中的某个 Filter,可以手动关闭对应的 Filter。&lt;/p>
&lt;p>接入 Sentinel Dubbo Adapter 后,即使未配置规则,Sentinel 也会对相应的 Dubbo 服务的调用信息进行统计。那么我们怎么知道 Sentinel 接入成功了呢?这时候就要请出一大利器 —— Sentinel 控制台了。&lt;/p>
&lt;h2 id="限流必备---监控管理">限流必备 - 监控管理&lt;/h2>
&lt;p>流量具有很强的实时性,之所以需要限流,是因为我们无法对流量的到来作出精确的预判,不然的话我们完全可以通过弹性的计算资源来处理,所以这时候为了保证限流的准确性,限流框架的监控功能就非常重要了。&lt;/p>
&lt;p>Sentinel 的控制台(Dashboard)是流量控制、熔断降级规则统一配置和管理的入口,同时它为用户提供了多个维度的监控功能。在 Sentinel 控制台上,我们可以配置规则并实时查看流量控制效果。&lt;/p>
&lt;p>接入 Sentinel 控制台的步骤如下(&lt;strong>缺一不可&lt;/strong>):&lt;/p>
&lt;ol>
&lt;li>按照 &lt;a href="https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0">Sentinel 控制台文档&lt;/a> 启动控制台&lt;/li>
&lt;li>应用引入 &lt;code>sentinel-transport-simple-http&lt;/code> 依赖,以便控制台可以拉取对应应用的相关信息&lt;/li>
&lt;li>给应用添加相关的启动参数,启动应用。需要配置的参数有:
&lt;ul>
&lt;li>&lt;code>-Dcsp.sentinel.api.port&lt;/code>:客户端的 port,用于上报相关信息(默认为 8719)&lt;/li>
&lt;li>&lt;code>-Dcsp.sentinel.dashboard.server&lt;/code>:控制台的地址&lt;/li>
&lt;li>&lt;code>-Dproject.name&lt;/code>:应用名称,会在控制台中显示&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>注意某些环境下本地运行 Dubbo 服务还需要加上 &lt;code>-Djava.net.preferIPv4Stack=true&lt;/code> 参数。比如中 Service 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-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>-Djava.net.preferIPv4Stack&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#b58900">true&lt;/span> -Dcsp.sentinel.api.port&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">8720&lt;/span> -Dcsp.sentinel.dashboard.server&lt;span style="color:#719e07">=&lt;/span>localhost:8080 -Dproject.name&lt;span style="color:#719e07">=&lt;/span>dubbo-provider-demo
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这样在启动应用后就能在控制台找到对应的应用了。以下是常用功能:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>单台设备监控&lt;/strong>:当在机器列表中看到您的机器,就代表着已经成功接入控制台,可以查看单台设备的设备名称、IP地址、端口号、健康状态和心跳时间等信息。&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2018/07/27/sentinel/machinediscover.png" alt="Discovery">&lt;/p>
&lt;ul>
&lt;li>&lt;strong>链路监控&lt;/strong>:簇点链路实时的去拉取指定客户端资源的运行情况,它提供了两种展示模式,一种用书状结构展示资源的调用链路;另外一种则不区分调用链路展示资源的运行情况。通过链路监控,可以查看到每个资源的流控和降级的历史状态。&lt;/li>
&lt;/ul>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align:center">树状链路&lt;/th>
&lt;th style="text-align:left">平铺链路&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align:center">&lt;img src="https://dubbo.apache.org/imgs/blog/2018/07/27/sentinel/resourceTree.png" alt="resourceTree">&lt;/td>
&lt;td style="text-align:left">&lt;img src="https://dubbo.apache.org/imgs/blog/2018/07/27/sentinel/sentine_dashboard.gif" alt="cluster">&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;ul>
&lt;li>&lt;strong>聚合监控&lt;/strong>:同一个服务下的所有机器的簇点信息会被汇总,实现实时监控,精确度达秒级。&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/sentinel-dashboard-metrics.png" alt="秒级实时监控">&lt;/p>
&lt;ul>
&lt;li>&lt;strong>规则配置&lt;/strong>:可以查看已有的限流、降级和系统保护规则,并实时地进行配置。&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/sentinel-dashboard-view-rules.png" alt="规则配置">&lt;/p>
&lt;h2 id="sentinel-基于-dubbo-的最佳实践">Sentinel 基于 Dubbo 的最佳实践&lt;/h2>
&lt;blockquote>
&lt;p>具体 Demo 代码请见 &lt;a href="https://github.com/alibaba/Sentinel/tree/master/sentinel-demo/sentinel-demo-dubbo">sentinel-demo-dubbo&lt;/a>。&lt;/p>
&lt;/blockquote>
&lt;h3 id="service-provider">Service Provider&lt;/h3>
&lt;blockquote>
&lt;p>对服务提供方的流量控制可分为&lt;strong>服务提供方的自我保护能力&lt;/strong>和&lt;strong>服务提供方对服务消费方的请求分配能力&lt;/strong>两个维度。&lt;/p>
&lt;/blockquote>
&lt;p>Service Provider 用于向外界提供服务,处理各个消费者的调用请求。为了保护 Provider 不被激增的流量拖垮影响稳定性,可以给 Provider 配置 &lt;strong>QPS 模式&lt;/strong>的限流,这样当每秒的请求量超过设定的阈值时会自动拒绝多的请求。限流粒度可以是 &lt;em>服务接口&lt;/em> 和 &lt;em>服务方法&lt;/em> 两种粒度。若希望整个服务接口的 QPS 不超过一定数值,则可以为对应服务接口资源(resourceName 为&lt;strong>接口全限定名&lt;/strong>)配置 QPS 阈值;若希望服务的某个方法的 QPS 不超过一定数值,则可以为对应服务方法资源(resourceName 为&lt;strong>接口全限定名:方法签名&lt;/strong>)配置 QPS 阈值。有关配置详情请参考 &lt;a href="https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6">流量控制 | Sentinel&lt;/a>。&lt;/p>
&lt;p>我们看一下这种模式的限流产生的效果。假设我们已经定义了某个服务接口 &lt;code>com.alibaba.csp.sentinel.demo.dubbo.FooService&lt;/code>,其中有一个方法 &lt;code>sayHello(java.lang.String)&lt;/code>,Provider 端该方法设定 QPS 阈值为 10。在 Consumer 端在 1s 之内连续发起 15 次调用,可以通过日志文件看到 Provider 端被限流。拦截日志统一记录在 &lt;code>~/logs/csp/sentinel-block.log&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>2018-07-24 17:13:43|1|com.alibaba.csp.sentinel.demo.dubbo.FooService:sayHello(java.lang.String),FlowException,default,|5,0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在 Provider 对应的 metrics 日志中也有记录:&lt;/p>
&lt;div class="highlight">&lt;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>1532423623000|2018-07-24 17:13:43|com.alibaba.csp.sentinel.demo.dubbo.FooService|15|0|15|0|3
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>1532423623000|2018-07-24 17:13:43|com.alibaba.csp.sentinel.demo.dubbo.FooService:sayHello(java.lang.String)|10|5|10|0|0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>根据&lt;strong>调用方&lt;/strong>的需求来分配服务提供方的处理能力也是常见的限流方式。比如有两个服务 A 和 B 都向 Service Provider 发起调用请求,我们希望只对来自服务 B 的请求进行限流,则可以设置限流规则的 &lt;code>limitApp&lt;/code> 为服务 B 的名称。Sentinel Dubbo Adapter 会自动解析 Dubbo 消费者(调用方)的 application name 作为调用方名称(&lt;code>origin&lt;/code>),在进行资源保护的时候都会带上调用方名称。若限流规则未配置调用方(&lt;code>default&lt;/code>),则该限流规则对所有调用方生效。若限流规则配置了调用方则限流规则将仅对指定调用方生效。&lt;/p>
&lt;blockquote>
&lt;p>注:Dubbo 默认通信不携带对端 application name 信息,因此需要开发者在调用端手动将 application name 置入 attachment 中,provider 端进行相应的解析。Sentinel Dubbo Adapter 实现了一个 Filter 用于自动从 consumer 端向 provider 端透传 application name。若调用端未引入 Sentinel Dubbo Adapter,又希望根据调用端限流,可以在调用端手动将 application name 置入 attachment 中,key 为 &lt;code>dubboApplication&lt;/code>。&lt;/p>
&lt;/blockquote>
&lt;p>在限流日志中会也会记录调用方的名称,如下面的日志中的 &lt;code>demo-consumer&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>2018-07-25 16:26:48|1|com.alibaba.csp.sentinel.demo.dubbo.FooService:sayHello(java.lang.String),FlowException,default,demo-consumer|5,0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="service-consumer">Service Consumer&lt;/h3>
&lt;blockquote>
&lt;p>对服务提供方的流量控制可分为&lt;strong>控制并发线程数&lt;/strong>和&lt;strong>服务降级&lt;/strong>两个维度。&lt;/p>
&lt;/blockquote>
&lt;h4 id="并发线程数限流">并发线程数限流&lt;/h4>
&lt;p>Service Consumer 作为客户端去调用远程服务。每一个服务都可能会依赖几个下游服务,若某个服务 A 依赖的下游服务 B 出现了不稳定的情况,服务 A 请求 服务 B 的响应时间变长,从而服务 A 调用服务 B 的线程就会产生堆积,最终可能耗尽服务 A 的线程数。我们通过用并发线程数来控制对下游服务 B 的访问,来保证下游服务不可靠的时候,不会拖垮服务自身。基于这种场景,推荐给 Consumer 配置&lt;strong>线程数模式&lt;/strong>的限流,来保证自身不被不稳定服务所影响。采用基于线程数的限流模式后,我们不需要再显式地去进行线程池隔离,Sentinel 会控制资源的线程数,超出的请求直接拒绝,直到堆积的线程处理完成,可以达到&lt;strong>信号量隔离&lt;/strong>的效果。&lt;/p>
&lt;p>我们看一下这种模式的效果。假设当前服务 A 依赖两个远程服务方法 &lt;code>sayHello(java.lang.String)&lt;/code> 和 &lt;code>doAnother()&lt;/code>。前者远程调用的响应时间 为 1s-1.5s 之间,后者 RT 非常小(30 ms 左右)。服务 A 端设两个远程方法 thread count 为 5。然后每隔 50 ms 左右向线程池投入两个任务,作为消费者分别远程调用对应方法,持续 10 次。可以看到 &lt;code>sayHello&lt;/code> 方法被限流 5 次,因为后面调用的时候前面的远程调用还未返回(RT 高);而 &lt;code>doAnother()&lt;/code> 调用则不受影响。线程数目超出时快速失败能够有效地防止自己被慢调用所影响。&lt;/p>
&lt;h4 id="服务降级">服务降级&lt;/h4>
&lt;p>当服务依赖于多个下游服务,而某个下游服务调用非常慢时,会严重影响当前服务的调用。这里我们可以利用 Sentinel 熔断降级的功能,为调用端配置基于平均 RT 的&lt;a href="https://github.com/alibaba/Sentinel/wiki/%E7%86%94%E6%96%AD%E9%99%8D%E7%BA%A7">降级规则&lt;/a>。这样当调用链路中某个服务调用的平均 RT 升高,在一定的次数内超过配置的 RT 阈值,Sentinel 就会对此调用资源进行降级操作,接下来的调用都会立刻拒绝,直到过了一段设定的时间后才恢复,从而保护服务不被调用端短板所影响。同时可以配合 fallback 功能使用,在被降级的时候提供相应的处理逻辑。&lt;/p>
&lt;h3 id="fallback">Fallback&lt;/h3>
&lt;p>从 0.1.1 版本开始,Sentinel Dubbo Adapter 还支持配置全局的 fallback 函数,可以在 Dubbo 服务被限流/降级/负载保护的时候进行相应的 fallback 处理。用户只需要实现自定义的 &lt;a href="https://github.com/alibaba/Sentinel/blob/master/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallback.java">&lt;code>DubboFallback&lt;/code>&lt;/a> 接口,并通过 &lt;code>DubboFallbackRegistry&lt;/code> 注册即可。默认情况会直接将 &lt;code>BlockException&lt;/code> 包装后抛出。同时,我们还可以配合 &lt;a href="https://dubbo.apache.org/zh-cn/docsv2.7/user/examples/local-mock/">Dubbo 的 fallback 机制&lt;/a> 来为降级的服务提供替代的实现。&lt;/p>
&lt;h2 id="sentinel-与-hystrix-的比较">Sentinel 与 Hystrix 的比较&lt;/h2>
&lt;p>目前业界常用的熔断降级/隔离的库是 Netflix 的 &lt;a href="https://github.com/Netflix/Hystrix">Hystrix&lt;/a>,那么 Sentinel 与 Hystrix 有什么异同呢?Hystrix 的关注点在于以 &lt;em>隔离&lt;/em> 和 &lt;em>熔断&lt;/em> 为主的容错机制,而 Sentinel 的侧重点在于多样化的流量控制、熔断降级、系统负载保护、实时监控和控制台,可以看到解决的问题还是有比较大的不同的。&lt;/p>
&lt;p>Hystrix 采用命令模式封装资源调用逻辑,并且资源的定义与隔离规则是强依赖的,即在创建 HystrixCommand 的时候就要指定隔离规则(因其执行模型依赖于隔离模式)。Sentinel 的设计更为简单,不关注资源是如何执行的,资源的定义与规则的配置相分离。用户可以先定义好资源,然后在需要的时候配置规则即可。Sentinel 的原则非常简单:根据对应资源配置的规则来为资源执行相应的限流/降级/负载保护策略,若规则未配置则仅进行统计。从 0.1.1 版本开始,Sentinel 还引入了&lt;a href="https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81">注解支持&lt;/a>,可以更方便地定义资源。&lt;/p>
&lt;p>隔离是 Hystrix 的核心功能。Hystrix 通过线程池或信号量的方式来对依赖(即 Sentinel 中对应的资源)进行隔离,其中最常用的是资源隔离。Hystrix 线程池隔离的好处是比较彻底,但是不足之处在于要开很多线程池,在应用本身线程数目比较多的时候上下文切换的 overhead 会非常大;Hystrix 的信号量隔离模式可以限制调用的并发数而不显式创建线程,这样的方式比较轻量级,但缺点是无法对慢调用自动进行降级,只能等待客户端自己超时,因此仍然可能会出现级联阻塞的情况。Sentinel 可以通过并发线程数模式的流量控制来提供信号量隔离的功能。并且结合基于响应时间的熔断降级模式,可以在不稳定资源的平均响应时间比较高的时候自动降级,防止过多的慢调用占满并发数,影响整个系统。&lt;/p>
&lt;p>Hystrix 熔断降级功能采用熔断器模式,在某个服务失败比率高时自动进行熔断。Sentinel 的熔断降级功能更为通用,支持平均响应时间与失败比率两个指标。Sentinel 还提供各种调用链路关系和流量控制效果支持,同时还可以根据系统负载去实时地调整流量来保护系统,应用场景更为丰富。同时,Sentinel 还提供了实时的监控 API 和控制台,可以方便用户快速了解目前系统的状态,对服务的稳定性了如指掌。&lt;/p>
&lt;p>更详细的对比请参见 &lt;a href="https://github.com/alibaba/Sentinel/wiki/Sentinel-%E4%B8%8E-Hystrix-%E7%9A%84%E5%AF%B9%E6%AF%94">Sentinel 与 Hystrix 的对比&lt;/a>。&lt;/p>
&lt;h2 id="总结">总结&lt;/h2>
&lt;p>以上介绍的只是 Sentinel 的一个最简单的场景 —— 限流。Sentinel 还可以处理更复杂的各种情况,比如超时熔断、冷启动、请求匀速等。可以参考 &lt;a href="https://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E9%A1%B5">Sentinel 文档&lt;/a>,更多的场景等待你去挖掘!&lt;/p></description></item><item><title>Blog: 使用Pinpoint做分布式跟踪</title><link>https://dubbo.apache.org/zh-cn/blog/2018/07/12/%E4%BD%BF%E7%94%A8pinpoint%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E8%B7%9F%E8%B8%AA/</link><pubDate>Thu, 12 Jul 2018 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2018/07/12/%E4%BD%BF%E7%94%A8pinpoint%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E8%B7%9F%E8%B8%AA/</guid><description>
&lt;p>在使用Dubbo进行服务化或者整合应用后,假设某个服务后台日志显示有异常,这个服务又被多个应用调用的情况下,我们通常很难判断是哪个应用调用的,问题的起因是什么,因此我们需要一套分布式跟踪系统来快速定位问题,Pinpoint可以帮助我们快速定位问题(当然,解决方案也不止这一种)。&lt;/p>
&lt;h2 id="什么是pinpoint">什么是Pinpoint&lt;/h2>
&lt;blockquote>
&lt;p>摘自&lt;a href="https://skyao.gitbooks.io/learning-pinpoint/content/index.html">Pinpoint学习笔记&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;p>&lt;a href="https://github.com/naver/pinpoint">Pinpoint&lt;/a>是一个开源的 APM (Application Performance Management/应用性能管理)工具,用于基于java的大规模分布式系统。
仿照Google Dapper,Pinpoint通过跟踪分布式应用之间的调用来提供解决方案,以帮助分析系统的总体结构和内部模块之间如何相互联系。&lt;/p>
&lt;blockquote>
&lt;p>注:对于各个模块之间的通讯英文原文中用的是transaction一词,但是我觉得如果翻译为&amp;quot;事务&amp;quot;容易引起误解,所以替换为&amp;quot;交互&amp;quot;或者&amp;quot;调用&amp;quot;这种比较直白的字眼。&lt;/p>
&lt;/blockquote>
&lt;p>在使用上力图简单高效:&lt;/p>
&lt;ul>
&lt;li>安装agent,不需要修改哪怕一行代码&lt;/li>
&lt;li>最小化性能损失&lt;/li>
&lt;/ul>
&lt;h3 id="服务器地图servermap">服务器地图(ServerMap)&lt;/h3>
&lt;p>通过可视化分布式系统的模块和他们之间的相互联系来理解系统拓扑。点击某个节点会展示这个模块的详情,比如它当前的状态和请求数量。&lt;/p>
&lt;h3 id="实时活动线程图表realtime-active-thread-chart">实时活动线程图表(Realtime Active Thread Chart)&lt;/h3>
&lt;p>实时监控应用内部的活动线程。&lt;/p>
&lt;h3 id="请求应答分布图表requestresponse-scatter-chart">请求/应答分布图表(Request/Response Scatter Chart)&lt;/h3>
&lt;p>长期可视化请求数量和应答模式来定位潜在问题。通过在图表上拉拽可以选择请求查看更多的详细信息。&lt;/p>
&lt;h3 id="调用栈callstack">调用栈(CallStack)&lt;/h3>
&lt;p>在分布式环境中为每个调用生成代码级别的可视图,在单个视图中定位瓶颈和失败点。&lt;/p>
&lt;h3 id="巡查inspector">巡查(Inspector)&lt;/h3>
&lt;p>查看应用上的其他详细信息,比如CPU使用率,内存/垃圾回收,TPS,和JVM参数。&lt;/p>
&lt;h3 id="支持模块">支持模块&lt;/h3>
&lt;ul>
&lt;li>JDK 6+&lt;/li>
&lt;li>Tomcat 6/7/8, Jetty 8/9, JBoss EAP 6, Resin 4, Websphere 6/7/8, Vertx 3.3/3.4/3.5&lt;/li>
&lt;li>Spring, Spring Boot (Embedded Tomcat, Jetty)&lt;/li>
&lt;li>Apache HTTP Client 3.x/4.x, JDK HttpConnector, GoogleHttpClient, OkHttpClient, NingAsyncHttpClient&lt;/li>
&lt;li>Thrift Client, Thrift Service, DUBBO PROVIDER, DUBBO CONSUMER&lt;/li>
&lt;li>ActiveMQ, RabbitMQ&lt;/li>
&lt;li>MySQL, Oracle, MSSQL, CUBRID,POSTGRESQL, MARIA&lt;/li>
&lt;li>Arcus, Memcached, Redis, CASSANDRA&lt;/li>
&lt;li>iBATIS, MyBatis&lt;/li>
&lt;li>DBCP, DBCP2, HIKARICP&lt;/li>
&lt;li>gson, Jackson, Json Lib&lt;/li>
&lt;li>log4j, Logback&lt;/li>
&lt;li>自定义模块&lt;/li>
&lt;/ul>
&lt;h2 id="pinpoint与dubbo的结合">Pinpoint与Dubbo的结合&lt;/h2>
&lt;h3 id="启动pinpoint">启动Pinpoint&lt;/h3>
&lt;p>参考Pinpoint的&lt;a href="https://pinpoint-apm.github.io/pinpoint/quickstart.html">Quick start&lt;/a>搭建环境(不需要启动TestApp)&lt;/p>
&lt;h3 id="准备dubbo示例程序">准备Dubbo示例程序&lt;/h3>
&lt;h4 id="创建api包">创建API包&lt;/h4>
&lt;p>pom.xml&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#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;project&lt;/span> xmlns=&lt;span style="color:#2aa198">&amp;#34;http://maven.apache.org/POM/4.0.0&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://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.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;modelVersion&amp;gt;&lt;/span>4.0.0&lt;span style="color:#268bd2">&amp;lt;/modelVersion&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;groupId&amp;gt;&lt;/span>com.example&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>demo-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;version&amp;gt;&lt;/span>0.0.1-SNAPSHOT&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;/project&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>新建API接口:&lt;/p>
&lt;div class="highlight">&lt;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.example.demoapi;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>public &lt;span style="color:#268bd2">interface&lt;/span> HelloService {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHello&lt;/span>(String name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="实现-dubbo-服务提供方">实现 Dubbo 服务提供方&lt;/h4>
&lt;p>pom.xml&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#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;project&lt;/span> xmlns=&lt;span style="color:#2aa198">&amp;#34;http://maven.apache.org/POM/4.0.0&amp;#34;&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://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.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;modelVersion&amp;gt;&lt;/span>4.0.0&lt;span style="color:#268bd2">&amp;lt;/modelVersion&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;groupId&amp;gt;&lt;/span>com.example&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>demo-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;version&amp;gt;&lt;/span>0.0.1-SNAPSHOT&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;packaging&amp;gt;&lt;/span>jar&lt;span style="color:#268bd2">&amp;lt;/packaging&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;name&amp;gt;&lt;/span>demo-provider&lt;span style="color:#268bd2">&amp;lt;/name&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;parent&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>org.springframework.boot&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>spring-boot-starter-parent&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.0.3.RELEASE&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;relativePath/&amp;gt;&lt;/span> &lt;span style="color:#586e75">&amp;lt;!-- lookup parent from repository --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/parent&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;properties&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;project.build.sourceEncoding&amp;gt;&lt;/span>UTF-8&lt;span style="color:#268bd2">&amp;lt;/project.build.sourceEncoding&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;project.reporting.outputEncoding&amp;gt;&lt;/span>UTF-8&lt;span style="color:#268bd2">&amp;lt;/project.reporting.outputEncoding&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;java.version&amp;gt;&lt;/span>1.8&lt;span style="color:#268bd2">&amp;lt;/java.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;repositories&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;repository&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;id&amp;gt;&lt;/span>sonatype-nexus-snapshots&lt;span style="color:#268bd2">&amp;lt;/id&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;url&amp;gt;&lt;/span>https://oss.sonatype.org/content/repositories/snapshots&lt;span style="color:#268bd2">&amp;lt;/url&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;releases&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;enabled&amp;gt;&lt;/span>false&lt;span style="color:#268bd2">&amp;lt;/enabled&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/releases&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;snapshots&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;enabled&amp;gt;&lt;/span>true&lt;span style="color:#268bd2">&amp;lt;/enabled&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/snapshots&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/repository&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/repositories&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>org.springframework.boot&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>spring-boot-starter&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>com.alibaba.boot&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-spring-boot-starter&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.2.0&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>com.example&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>demo-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;version&amp;gt;&lt;/span>0.0.1-SNAPSHOT&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.springframework.boot&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>spring-boot-starter-test&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;scope&amp;gt;&lt;/span>test&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>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;build&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;plugins&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;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>org.springframework.boot&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>spring-boot-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;/plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/plugins&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/build&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;/project&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol>
&lt;li>实现 &lt;code>HelloService&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-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> com.example.demoprovider.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.example.demoapi.HelloService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Service&lt;/span>&lt;span style="color:#719e07">(&lt;/span>version &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;${demo.service.version}&amp;#34;&lt;/span>&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> &lt;span style="color:#2aa198">&amp;#34;${dubbo.application.id}&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> protocol &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;${dubbo.protocol.id}&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registry &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;${dubbo.registry.id}&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">HelloServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> HelloService &lt;span 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:#dc322f">int&lt;/span> i &lt;span style="color:#719e07">=&lt;/span> 0&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">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> 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>i &lt;span style="color:#719e07">%&lt;/span> 3 &lt;span style="color:#719e07">==&lt;/span> 0&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RuntimeException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;ex&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:#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;!&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="2">
&lt;li>编写 Spring Boot 引导程序:&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:#719e07">package&lt;/span> com.example.demoprovider&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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.springframework.boot.SpringApplication&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.boot.autoconfigure.SpringBootApplication&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&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">DemoProviderApplication&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/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>DemoProviderApplication&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 style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="3">
&lt;li>配置 &lt;code>application.properties&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-properties" data-lang="properties">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># Spring boot application&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>spring.application.name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">dubbo-provider-demo&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>server.port &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">9090&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>management.port &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">9091&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># Service version&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>demo.service.version &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">1.0.0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># Base packages to scan Dubbo Components (e.g @Service , @Reference)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.scan.basePackages &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">com.example.demoprovider&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># Dubbo Config properties&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.application.id &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">dubbo-provider-demo&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-provider-demo&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">## ProtocolConfig Bean&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.protocol.id &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.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">12345&lt;/span>
&lt;/span>&lt;/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.id &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">my-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">N/A&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="实现-dubbo-服务消费方">实现 Dubbo 服务消费方&lt;/h4>
&lt;p>pom.xml&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#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;project&lt;/span> xmlns=&lt;span style="color:#2aa198">&amp;#34;http://maven.apache.org/POM/4.0.0&amp;#34;&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://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.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;modelVersion&amp;gt;&lt;/span>4.0.0&lt;span style="color:#268bd2">&amp;lt;/modelVersion&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;groupId&amp;gt;&lt;/span>com.example&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>demo-consumer&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;version&amp;gt;&lt;/span>0.0.1-SNAPSHOT&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;packaging&amp;gt;&lt;/span>jar&lt;span style="color:#268bd2">&amp;lt;/packaging&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;name&amp;gt;&lt;/span>demo-consumer&lt;span style="color:#268bd2">&amp;lt;/name&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;parent&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>org.springframework.boot&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>spring-boot-starter-parent&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.0.3.RELEASE&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;relativePath/&amp;gt;&lt;/span> &lt;span style="color:#586e75">&amp;lt;!-- lookup parent from repository --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/parent&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;properties&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;project.build.sourceEncoding&amp;gt;&lt;/span>UTF-8&lt;span style="color:#268bd2">&amp;lt;/project.build.sourceEncoding&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;project.reporting.outputEncoding&amp;gt;&lt;/span>UTF-8&lt;span style="color:#268bd2">&amp;lt;/project.reporting.outputEncoding&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;java.version&amp;gt;&lt;/span>1.8&lt;span style="color:#268bd2">&amp;lt;/java.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;repositories&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;repository&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;id&amp;gt;&lt;/span>sonatype-nexus-snapshots&lt;span style="color:#268bd2">&amp;lt;/id&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;url&amp;gt;&lt;/span>https://oss.sonatype.org/content/repositories/snapshots&lt;span style="color:#268bd2">&amp;lt;/url&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;releases&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;enabled&amp;gt;&lt;/span>false&lt;span style="color:#268bd2">&amp;lt;/enabled&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/releases&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;snapshots&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;enabled&amp;gt;&lt;/span>true&lt;span style="color:#268bd2">&amp;lt;/enabled&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/snapshots&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/repository&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/repositories&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>org.springframework.boot&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>spring-boot-starter-web&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>com.alibaba.boot&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-spring-boot-starter&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.2.0&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>com.example&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>demo-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;version&amp;gt;&lt;/span>0.0.1-SNAPSHOT&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.springframework.boot&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>spring-boot-starter-test&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;scope&amp;gt;&lt;/span>test&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>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;build&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;plugins&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;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>org.springframework.boot&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>spring-boot-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;configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;classifier&amp;gt;&lt;/span>exec&lt;span style="color:#268bd2">&amp;lt;/classifier&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;/plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/plugins&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/build&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;/project&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol>
&lt;li>通过 &lt;code>@Reference&lt;/code> 注入 &lt;code>HelloService&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-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> com.example.democonsumer.controller&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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.example.demoapi.HelloService&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.web.bind.annotation.RequestMapping&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.web.bind.annotation.RequestParam&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.web.bind.annotation.RestController&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@RestController&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DemoConsumerController&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 style="color:#719e07">(&lt;/span>version &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;${demo.service.version}&amp;#34;&lt;/span>&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> &lt;span style="color:#2aa198">&amp;#34;${dubbo.application.id}&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> url &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;dubbo://&amp;lt;注意,这里填写具体IP&amp;gt;: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> HelloService helloService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@RequestMapping&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;/sayHello&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&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>&lt;span style="color:#268bd2">@RequestParam&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> helloService&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;blockquote>
&lt;p>直连提供者调用需要填写具体IP地址,如果写localhost也可以,但是会被Pinpoint额外识别为一个未知服务&lt;/p>
&lt;/blockquote>
&lt;ol start="2">
&lt;li>编写 Spring Boot 引导程序(Web 应用):&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:#719e07">package&lt;/span> com.example.democonsumer&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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.springframework.boot.SpringApplication&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.boot.autoconfigure.SpringBootApplication&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&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">DemoConsumerApplication&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/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>DemoConsumerApplication&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 style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="3">
&lt;li>配置 &lt;code>application.properties&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-properties" data-lang="properties">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># Spring boot application&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>spring.application.name&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">dubbo-consumer-demo&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>server.port&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">8080&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>management.port&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">8081&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># Service Version&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>demo.service.version&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">1.0.0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># Dubbo Config properties&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.application.id&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">dubbo-consumer-demo&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-consumer-demo&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">## ProtocolConfig Bean&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.protocol.id&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.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">12345&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="使用pinpoint-agent启动服务提供方和服务消费方">使用Pinpoint-agent启动服务提供方和服务消费方&lt;/h3>
&lt;h4 id="启动服务提供方">启动服务提供方&lt;/h4>
&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-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>mvn clean package
&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-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>java -jar -javaagent:$AGENT_PATH/pinpoint-bootstrap-$VERSION.jar -Dpinpoint.agentId=demo-provider -Dpinpoint.applicationName=DP target/demo-provider-0.0.1-SNAPSHOT.jar
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="3">
&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-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>java -jar -javaagent:$AGENT_PATH/pinpoint-bootstrap-$VERSION.jar -Dpinpoint.agentId=demo-consumer -Dpinpoint.applicationName=DC target/demo-comsumer-0.0.1-SNAPSHOT-exec.jar
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="4">
&lt;li>访问消费方地址模拟用户请求&lt;/li>
&lt;/ol>
&lt;p>&lt;code>http://localhost:8080/sayHello?name=ABC&lt;/code>&lt;/p>
&lt;h2 id="使用pinpoint快速定位问题">使用Pinpoint快速定位问题&lt;/h2>
&lt;h3 id="首页">首页&lt;/h3>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/pinpoint-home.png" alt="/admin-guide/images/pinpoint-home.png">&lt;/p>
&lt;blockquote>
&lt;p>这里的用户请求是请求DubboProvider数量的双倍,原因是记录了favicon.ico图标请求导致的&lt;/p>
&lt;/blockquote>
&lt;h3 id="调用树">调用树&lt;/h3>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/pinpoint-calltree.png" alt="/admin-guide/images/pinpoint-calltree.png">&lt;/p>
&lt;h3 id="深入跟踪">深入跟踪&lt;/h3>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/pinpoint-mixedview.png" alt="/admin-guide/images/pinpoint-mixedview.png">&lt;/p>
&lt;h3 id="其他">其他&lt;/h3>
&lt;p>示例简单的模拟了Dubbo的提供和调用,并没有进行数据库等其他中间件的应用,详细使用请参照Pinpoint文档。&lt;/p></description></item><item><title>Blog: 在 Dubbo 中使用 Zipkin</title><link>https://dubbo.apache.org/zh-cn/blog/2018/06/17/%E5%9C%A8-dubbo-%E4%B8%AD%E4%BD%BF%E7%94%A8-zipkin/</link><pubDate>Sun, 17 Jun 2018 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2018/06/17/%E5%9C%A8-dubbo-%E4%B8%AD%E4%BD%BF%E7%94%A8-zipkin/</guid><description>
&lt;p>随着业务的发展,应用的规模不断的扩大,传统的应用架构无法满足诉求,服务化架构改造势在必行,以 Dubbo 为代表的分布式服务框架成为了服务化改造架构中的基石。随着微服务理念逐渐被大众接受,应用进一步向更细粒度拆分,并且,不同的应用由不同的开发团队独立负责,整个分布式系统变得十分复杂。没有人能够清晰及时的知道当前系统整体的依赖关系。当出现问题时,也无法及时知道具体是链路上的哪个环节出了问题。&lt;/p>
&lt;p>在这个背景下,Google 发表了 &lt;a href="https://ai.google/research/pubs/pub36356">Dapper&lt;/a> 的论文,描述了如何通过一个分布式追踪系统解决上述问题。基于该论文,各大互联网公司实现并部署了自己的分布式追踪系统,其中比较出名的有阿里巴巴的 EagleEye。本文中提到的 Zipkin 是 Twitter 公司开源的分布式追踪系统。下面会详细介绍如何在 Dubbo 中使用 Zipkin 来实现分布式追踪。&lt;/p>
&lt;h2 id="zipkin-简介">Zipkin 简介&lt;/h2>
&lt;p>Zipkin 是基于 &lt;a href="https://ai.google/research/pubs/pub36356">Dapper&lt;/a> 论文实现,由 Twitter 开源的分布式追踪系统,通过收集分布式服务执行时间的信息来达到追踪服务调用链路、以及分析服务执行延迟等目的。&lt;/p>
&lt;h3 id="zipkin-架构">Zipkin 架构&lt;/h3>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/zipkin-architecture.png" alt="Zipkin architecture">&lt;/p>
&lt;p>Collector 收集器、Storage 存储、API、UI 用户界面等几部分构成了 Zipkin Server 部分,对应于 GitHub 上 &lt;a href="https://github.com/openzipkin/zipkin">openzipkin/zipkin&lt;/a> 这个项目。而收集应用中调用的耗时信息并将其上报的组件与应用共生,并拥有各个语言的实现版本,其中 Java 的实现是 GitHub 上 &lt;a href="https://github.com/openzipkin/brave">openzipkin/brave&lt;/a>。除了 Java 客户端实现之外,openzipkin 还提供了许多其他语言的实现,其中包括了 go、php、JavaScript、.net、ruby 等,具体列表可以参阅 Zipkin 的 &lt;a href="https://zipkin.io/pages/tracers_instrumentation.html">Exiting instrumentations&lt;/a>。&lt;/p>
&lt;h3 id="zipkin-的工作过程">Zipkin 的工作过程&lt;/h3>
&lt;p>当用户发起一次调用时,Zipkin 的客户端会在入口处为整条调用链路生成一个全局唯一的 trace id,并为这条链路中的每一次分布式调用生成一个 span id。span 与 span 之间可以有父子嵌套关系,代表分布式调用中的上下游关系。span 和 span 之间可以是兄弟关系,代表当前调用下的两次子调用。一个 trace 由一组 span 组成,可以看成是由 trace 为根节点,span 为若干个子节点的一棵树。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/trace-sample.png" alt="Related image">&lt;/p>
&lt;p>Span 由调用边界来分隔,在 Zipkin 中,调用边界由以下四个 annotation 来表示:&lt;/p>
&lt;ul>
&lt;li>cs - Clent Sent 客户端发送了请求&lt;/li>
&lt;li>sr - Server Receive 服务端接受到请求&lt;/li>
&lt;li>ss - Server Send 服务端处理完毕,向客户端发送回应&lt;/li>
&lt;li>cr - Client Receive 客户端收到结果&lt;/li>
&lt;/ul>
&lt;p>显然,通过这四个 annotation 上的时间戳,可以轻易的知道一次完整的调用在不同阶段的耗时,比如:&lt;/p>
&lt;ul>
&lt;li>sr - cs 代表了请求在网络上的耗时&lt;/li>
&lt;li>ss - sr 代表了服务端处理请求的耗时&lt;/li>
&lt;li>cr - ss 代表了回应在网络上的耗时&lt;/li>
&lt;li>cr - cs 代表了一次调用的整体耗时&lt;/li>
&lt;/ul>
&lt;p>Zipkin 会将 trace 相关的信息在调用链路上传递,并在每个调用边界结束时异步的把当前调用的耗时信息上报给 Zipkin Server。Zipkin Server 在收到 trace 信息后,将其存储起来,Zipkin 支持的存储类型有 inMemory、MySql、Cassandra、以及 ElasticsSearch 几种方式。随后 Zipkin 的 Web UI 会通过 API 访问的方式从存储中将 trace 信息提取出来分析并展示,如下图所示:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/zipkin-web-screenshot.png" alt="Web interface screenshot">&lt;/p>
&lt;h2 id="在-dubbo-中使用">在 Dubbo 中使用&lt;/h2>
&lt;p>由于 &lt;a href="https://github.com/openzipkin/brave">Brave&lt;/a> 对 Dubbo 已经主动做了支持,在 Dubbo 中集成基于 Zipkin 的链路追踪变的十分简单。下面会按照 Brave 中关于 &lt;a href="https://github.com/openzipkin/brave/blob/master/instrumentation/dubbo-rpc/README.md">Dubbo RPC 支持的指引&lt;/a>来说明如何在 Dubbo 中使用 Zipkin。&lt;/p>
&lt;h3 id="安装-zipkin-server">安装 Zipkin Server&lt;/h3>
&lt;p>按照 &lt;a href="https://github.com/openzipkin/zipkin/tree/master/zipkin-server#quick-start">Zipkin 官方文档中的快速开始&lt;/a> 来安装 Zipkin,如下所示:&lt;/p>
&lt;div class="highlight">&lt;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 -sSL https://zipkin.io/quickstart.sh | bash -s
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$ java -jar zipkin.jar
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>按照这种方式安装的 Zipkin Server 使用的存储类型是 inMemory 的。当服务器停机之后,所有收集到的 trace 信息会丢失,不适用于生产系统。如果在生产系统中使用,需要配置另外的存储类型。Zipkin 支持 MySql、Cassandra、和 ElasticSearch。推荐使用 Cassandra 和 ElasticSearch,相关的配置请自行查阅&lt;a href="https://github.com/openzipkin/zipkin/tree/master/zipkin-server">官方文档&lt;/a>。&lt;/p>
&lt;p>本文为了演示方便,使用的存储是 inMemory 类型。成功启动之后,可以在终端看到如下的提示:&lt;/p>
&lt;div class="highlight">&lt;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>$ java -jar zipkin.jar
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Picked up JAVA_TOOL_OPTIONS: -Djava.awt.headless&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#b58900">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ********
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ** **
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> * *
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ** **
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ** **
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ** **
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ** **
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ********
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ****
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ****
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> **** ****
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ****** **** ***
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ****************************************************************************
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ******* **** ***
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> **** ****
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> **
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> **
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ***** ** ***** ** ** ** ** **
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ** ** ** * *** ** **** **
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ** ** ***** **** ** ** ***
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ****** ** ** ** ** ** ** **
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>:: Powered by Spring Boot :: &lt;span style="color:#719e07">(&lt;/span>v2.0.5.RELEASE&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>o.s.b.w.e.u.UndertowServletWebServer : Undertow started on port&lt;span style="color:#719e07">(&lt;/span>s&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#2aa198">9411&lt;/span> &lt;span style="color:#719e07">(&lt;/span>http&lt;span style="color:#719e07">)&lt;/span> with context path &lt;span style="color:#2aa198">&amp;#39;&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>2018-10-10 18:40:31.605 INFO &lt;span style="color:#2aa198">21072&lt;/span> --- &lt;span style="color:#719e07">[&lt;/span> main&lt;span style="color:#719e07">]&lt;/span> z.s.ZipkinServer : Started ZipkinServer in 6.835 seconds &lt;span style="color:#719e07">(&lt;/span>JVM running &lt;span style="color:#719e07">for&lt;/span> 8.35&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>然后在浏览器中访问 http://localhost:9411 验证 WEB 界面。&lt;/p>
&lt;h3 id="配置-maven-依赖">配置 Maven 依赖&lt;/h3>
&lt;h4 id="引入-brave-依赖">引入 Brave 依赖&lt;/h4>
&lt;p>新建一个新的 Java 工程,并在 pom.xml 中引入 Brave 相关的依赖如下:&lt;/p>
&lt;div class="highlight">&lt;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;brave.version&amp;gt;&lt;/span>5.4.2&lt;span style="color:#268bd2">&amp;lt;/brave.version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;zipkin-reporter.version&amp;gt;&lt;/span>2.7.9&lt;span style="color:#268bd2">&amp;lt;/zipkin-reporter.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:#586e75">&amp;lt;!-- 引入 zipkin brave 的 BOM 文件 --&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.zipkin.brave&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>brave-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>${brave.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>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">&amp;lt;!-- 引入 zipkin repoter 的 BOM 文件 --&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.zipkin.reporter2&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>zipkin-reporter-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>${zipkin-reporter.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:#586e75">&amp;lt;!-- 1. brave 对 dubbo 的集成 --&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.zipkin.brave&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>brave-instrumentation-dubbo-rpc&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;!-- 2. brave 的 spring bean 支持 --&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.zipkin.brave&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>brave-spring-beans&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;!-- 3. 在 SLF4J 的 MDC (Mapped Diagnostic Context) 中支持 traceId 和 spanId --&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.zipkin.brave&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>brave-context-slf4j&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;!-- 4. 使用 okhttp3 作为 reporter --&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.zipkin.reporter2&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>zipkin-sender-okhttp3&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;p>其中:&lt;/p>
&lt;ol>
&lt;li>引入 brave-instrumentation-dubbo-rpc,brave 对 dubbo 的支持:https://github.com/openzipkin/brave/blob/master/instrumentation/dubbo-rpc/README.md&lt;/li>
&lt;li>引入 brave-spring-beans,brave 对 spring bean 的支持:https://github.com/openzipkin/brave/blob/master/spring-beans/README.md&lt;/li>
&lt;li>引入 brave-context-slf4j,brave 对 SLF4J 的支持,可以在 MDC 中使用 traceId 和 spanId:https://github.com/openzipkin/brave/blob/master/context/slf4j/README.md&lt;/li>
&lt;li>引入 zipkin-sender-okhttp3,使用 okhttp3 上报数据:https://github.com/openzipkin/zipkin-reporter-java&lt;/li>
&lt;/ol>
&lt;h4 id="引入-dubbo-相关依赖">引入 Dubbo 相关依赖&lt;/h4>
&lt;p>Dubbo 相关的依赖是 Dubbo 本身以及 Zookeeper 客户端,在下面的例子中,我们将会使用独立的 Zookeeper Server 作为服务发现。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dependencies&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">&amp;lt;!-- 1. Zookeeper 客户端依赖 --&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;version&amp;gt;&lt;/span>2.12.0&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;exclusions&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;exclusion&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&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;/exclusion&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/exclusions&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:#586e75">&amp;lt;!-- 2. Dubbo 依赖 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>com.alibaba&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>dubbo&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;version&amp;gt;&lt;/span>2.6.2&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/dependencies&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>其中:&lt;/p>
&lt;ol>
&lt;li>Dubbo 这里依赖独立的 Zookeeper Server 做服务发现,这里使用的客户端是 Curator&lt;/li>
&lt;li>引入 Dubbo 框架的依赖,原则上 2.6 的任何版本都是工作的,这里使用的是 2.6.2 版本&lt;/li>
&lt;/ol>
&lt;h3 id="实现">实现&lt;/h3>
&lt;p>我们这里构造的场景是一个有两个节点的服务依赖链,也就是,当一个 Dubbo 客户端调用服务 A 时,服务 A 将会继续调用服务 B。在这个例子中,服务 A 是 greeting service,它所依赖的下游服务服务 B 是 hello service。&lt;/p>
&lt;h4 id="定义服务接口">定义服务接口&lt;/h4>
&lt;p>为此需要事先定义两个服务接口 GreetingService 以及 HelloService&lt;/p>
&lt;ol>
&lt;li>
&lt;p>com.alibaba.dubbo.samples.api.GreetingService&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-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.samples.api&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/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">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">greeting&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String 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;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>com.alibaba.dubbo.samples.api.HelloService&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-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.samples.api&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/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">HelloService&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">hello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String 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;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;h4 id="实现服务接口">实现服务接口&lt;/h4>
&lt;p>为了区分对待,所有和 HelloService 相关的实现代码都放在 hello 子包下,同理 GreetingService 相关的放在 greeting 子包下。&lt;/p>
&lt;ol>
&lt;li>
&lt;p>实现 com.alibaba.dubbo.samples.api.HelloService&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-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.samples.service.hello&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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.samples.api.HelloService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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.Random&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/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">HelloServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> HelloService &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">hello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String message&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 通过 sleep 模拟业务逻辑处理时间
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Thread&lt;span style="color:#719e07">.&lt;/span>sleep&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> Random&lt;span style="color:#719e07">(&lt;/span>System&lt;span style="color:#719e07">.&lt;/span>currentTimeMillis&lt;span style="color:#719e07">()).&lt;/span>nextInt&lt;span style="color:#719e07">(&lt;/span>1000&lt;span style="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> &lt;span style="color:#586e75">// no op
&lt;/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:#2aa198">&amp;#34;hello, &amp;#34;&lt;/span> &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 style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>实现 com.alibaba.dubbo.samples.api.GreetingService&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-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.samples.service.greeting&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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.samples.api.GreetingService&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.samples.api.HelloService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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.Random&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/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">GreetingServiceImpl&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:#586e75">// 下游依赖服务,运行时靠 spring 容器注入 HelloService 的服务代理
&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> HelloService helloService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/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">setHelloService&lt;/span>&lt;span style="color:#719e07">(&lt;/span>HelloService helloService&lt;span style="color:#719e07">)&lt;/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>helloService &lt;span style="color:#719e07">=&lt;/span> helloService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="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">greeting&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String message&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 通过 sleep 模拟业务逻辑处理时间
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Thread&lt;span style="color:#719e07">.&lt;/span>sleep&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> Random&lt;span style="color:#719e07">(&lt;/span>System&lt;span style="color:#719e07">.&lt;/span>currentTimeMillis&lt;span style="color:#719e07">()).&lt;/span>nextInt&lt;span style="color:#719e07">(&lt;/span>1000&lt;span style="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> &lt;span style="color:#586e75">// no op
&lt;/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:#2aa198">&amp;#34;greeting, &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> helloService&lt;span style="color:#719e07">.&lt;/span>hello&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 style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里需要注意的是,GreetingServiceImpl 的实现中声明了一个类型是 HelloService 的成员变量,并在 greeting 方法中,执行完自己逻辑之后又调用了 HelloService 上的 hello 方法。这里的 helloService 的实现将会在运行态由外部注入,注入的不是 HelloServiceImpl 的实现,而是 HelloService 的远程调用代理。通过这样的方式,完成了在一个 Dubbo 服务中继续调用另一个远程 Dubbo 服务的目的。从链路追踪的角度来说,客户端调用 GreetingService 是一个 span,GreetingService 调用 HelloService 是另一个 span,并且两者有父子关系,同属于一个 trace,也就是属于同一条调用链路。&lt;/p>
&lt;p>另外,在 GreetingServiceImpl 和 HelloServiceImpl 的实现中,通过 Thread.sleep 来模拟了处理业务逻辑的耗时,以便在 Zipkin UI 上更好的展示。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h4 id="配置">配置&lt;/h4>
&lt;p>为了专注在展示如何使用 Zipkin 这一点上,本文在配置和编程模型上没有采用更多的高级技术,而是使用了最传统的 Spring XML 的配置方式,帮助读者理解。更高级的通过 annotation 甚至 spring boot 的方式,读者可以自行查阅 Dubbo 和 Zipkin 相关的文档。&lt;/p>
&lt;ol>
&lt;li>
&lt;p>暴露 HelloService 服务&lt;/p>
&lt;p>在 resouces/spring/hello-service.xml 中增加以下的配置来将 HelloServiceImpl 暴露成一个 Dubbo 服务:&lt;/p>
&lt;ul>
&lt;li>使用了本地启动的 Zookeeper Server 作为注册中心,地址为默认值 zookeeper://127.0.0.1:2181&lt;/li>
&lt;li>用 Dubbo 原生服务在端口 20880 上暴露服务&lt;/li>
&lt;li>将 HelloServiceImpl 注册成 id 是 &lt;code>helloService&lt;/code> 的 Spring Bean,这样就可以在后续的 &lt;code>&amp;lt;dubbo:service&amp;gt;&lt;/code> 中引用到这个实现类&lt;/li>
&lt;li>通过 &lt;code>&amp;lt;dubbo:service&amp;gt;&lt;/code> 将 HelloServiceImpl 暴露成 Dubbo 服务&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">&amp;lt;!-- 定义 HelloService 的应用名 --&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;hello-service-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> 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:#586e75">&amp;lt;!-- 使用 Dubbo 原生协议在 20880 端口上暴露服务 --&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>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">&amp;lt;!-- 将 HelloServiceImpl 的实现声明成一个 spring bean --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;bean&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;helloService&amp;#34;&lt;/span> class=&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.samples.service.hello.HelloServiceImpl&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;!-- 将 HelloServiceImpl 声明成一个 Dubbo 服务 --&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.HelloService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;helloService&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>增加 Zipkin 相关的配置&lt;/p>
&lt;p>在 resources/spring/hello-service.xml 中增加 Zipkin 相关的配置:&lt;/p>
&lt;ul>
&lt;li>修改 dubbo 服务暴露的配置,添加 Zipkin 的 tracing filter 到 Dubbo 的 filter chain 中&lt;/li>
&lt;li>按照 &lt;a href="https://github.com/openzipkin/brave/blob/master/spring-beans/README.md">https://github.com/openzipkin/brave/blob/master/spring-beans/README.md&lt;/a> 来配置 Zipkin 的 sender 和 tracing 的 spring bean&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:#586e75">&amp;lt;!-- 1. 修改 dubbo 服务暴露配置,在 filter chain 中增加 zipkin 的 tracing 过滤器 --&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.HelloService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;helloService&amp;#34;&lt;/span> filter=&lt;span style="color:#2aa198">&amp;#34;tracing&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;!-- 2. zipkin 相关的配置 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">&amp;lt;!-- 使用 OKHttp 来发送 trace 信息到 Zipkin Server。这里的 Zipkin Server 启动在本地 --&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;sender&amp;#34;&lt;/span> class=&lt;span style="color:#2aa198">&amp;#34;zipkin2.reporter.beans.OkHttpSenderFactoryBean&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;property&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;endpoint&amp;#34;&lt;/span> value=&lt;span style="color:#2aa198">&amp;#34;http://localhost:9411/api/v2/spans&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&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;tracing&amp;#34;&lt;/span> class=&lt;span style="color:#2aa198">&amp;#34;brave.spring.beans.TracingFactoryBean&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;property&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;localServiceName&amp;#34;&lt;/span> value=&lt;span style="color:#2aa198">&amp;#34;hello-service&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;property&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;spanReporter&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> class=&lt;span style="color:#2aa198">&amp;#34;zipkin2.reporter.beans.AsyncReporterFactoryBean&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;property&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;sender&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;sender&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;!-- wait up to half a second for any in-flight spans on close --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;property&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;closeTimeout&amp;#34;&lt;/span> value=&lt;span style="color:#2aa198">&amp;#34;500&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&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/property&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;property&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;currentTraceContext&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> class=&lt;span style="color:#2aa198">&amp;#34;brave.spring.beans.CurrentTraceContextFactoryBean&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;property&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;scopeDecorators&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> class=&lt;span style="color:#2aa198">&amp;#34;brave.context.slf4j.MDCScopeDecorator&amp;#34;&lt;/span> factory-method=&lt;span style="color:#2aa198">&amp;#34;create&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;/property&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/bean&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/property&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/bean&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>增加 HelloService 的启动类&lt;/p>
&lt;p>在 com.alibaba.dubbo.samples.service.hello.Application 中通过 ClassPathXmlApplicationContext 读取 刚才配置的 spring/hello-service.xml 来初始化一个 spring 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:#719e07">package&lt;/span> com.alibaba.dubbo.samples.service.hello&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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.springframework.context.support.ClassPathXmlApplicationContext&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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.io.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">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> 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/hello-service.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;Hello 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:#586e75">// press any key to exit
&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>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;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>暴露 GreetingService 服务,并使用 Zipkin&lt;/p>
&lt;p>在 resources/spring/greeting-service.xml 中配置 GreetingService。相关步骤与 HelloService 类似,不再赘述,重点关注如何在 GreetingService 中配置下游服务的依赖。完整的 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;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://dubbo.apache.org/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.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>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">&amp;lt;!-- 1. 定义 GreetingService 的应用名 --&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;greeting-service-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;!-- 2. 指定注册中心地址 --&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>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">&amp;lt;!-- 3. 使用 Dubbo 原生协议在 20881 端口上暴露服务 --&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;20881&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;!-- 4. 声明 HelloService 的远程代理,并在 Dubbo 的 filter chain 中增加 tracing filter --&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;helloService&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.api.HelloService&amp;#34;&lt;/span> filter=&lt;span style="color:#2aa198">&amp;#34;tracing&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;!-- 5. 将 GreetingServiceImpl 的实现声明成一个 spring bean,并将 HelloService 的远程代理装配进去 --&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;greetingService&amp;#34;&lt;/span> class=&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.samples.service.greeting.GreetingServiceImpl&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;property&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;helloService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;helloService&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&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;!-- 6. 将 GreetingServiceImpl 声明成一个 Dubbo 服务,并在 Dubbo 的 filter chain 中增加 tracing filter --&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.GreetingService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;greetingService&amp;#34;&lt;/span> filter=&lt;span style="color:#2aa198">&amp;#34;tracing&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;!-- 7. zipkin 相关的配置 --&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;sender&amp;#34;&lt;/span> class=&lt;span style="color:#2aa198">&amp;#34;zipkin2.reporter.beans.OkHttpSenderFactoryBean&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;property&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;endpoint&amp;#34;&lt;/span> value=&lt;span style="color:#2aa198">&amp;#34;http://localhost:9411/api/v2/spans&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&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;tracing&amp;#34;&lt;/span> class=&lt;span style="color:#2aa198">&amp;#34;brave.spring.beans.TracingFactoryBean&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;property&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;localServiceName&amp;#34;&lt;/span> value=&lt;span style="color:#2aa198">&amp;#34;greeting-service&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;property&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;spanReporter&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> class=&lt;span style="color:#2aa198">&amp;#34;zipkin2.reporter.beans.AsyncReporterFactoryBean&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;property&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;sender&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;sender&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;!-- wait up to half a second for any in-flight spans on close --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;property&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;closeTimeout&amp;#34;&lt;/span> value=&lt;span style="color:#2aa198">&amp;#34;500&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&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/property&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;property&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;currentTraceContext&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> class=&lt;span style="color:#2aa198">&amp;#34;brave.spring.beans.CurrentTraceContextFactoryBean&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;property&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;scopeDecorators&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> class=&lt;span style="color:#2aa198">&amp;#34;brave.context.slf4j.MDCScopeDecorator&amp;#34;&lt;/span> factory-method=&lt;span style="color:#2aa198">&amp;#34;create&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;/property&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/bean&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/property&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/bean&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;p>这里的配置与上面的 HelloService 类似,需要重点关注的有两点:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>第 3 步中注意服务需要暴露在不同的端口上,否则会和 HelloService 冲突,本例中选择的是 20881 这个端口&lt;/p>
&lt;/li>
&lt;li>
&lt;p>通过第 4 步先声明 HelloService 的远程代理,然后在第 5 步中将其组装给 GreetingService 来完成服务上下游依赖的声明&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>增加 GreeeingService 的启动类,与 HelloService 类似,通过 spring/greeting-service.xml 的配置来初始化一个新的 spring 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:#719e07">package&lt;/span> com.alibaba.dubbo.samples.service.greeting&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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.springframework.context.support.ClassPathXmlApplicationContext&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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.io.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">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> 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/greeting-service.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;Greeting 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:#586e75">// press any key to exit
&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>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;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>通过 resources/spring/client.xml 初始化一个 spring context,从其中获取 GreetingService 的远程代理,发起远程调用。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-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.samples.client&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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.samples.api.GreetingService&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.support.ClassPathXmlApplicationContext&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">Application&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&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/client.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:#586e75">// 获取远程代理并发起调用
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> GreetingService greetingService &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>GreetingService&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;greetingService&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>greetingService&lt;span style="color:#719e07">.&lt;/span>greeting&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:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>resource/spring/client.xml 中的配置与 Dubbo 服务的配置类似,主要是配置远程代理,以及配置 Zipkin&lt;/p>
&lt;div class="highlight">&lt;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://dubbo.apache.org/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.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>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">&amp;lt;!-- 1. 定义 dubbo 客户端的应用名 --&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-client&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;!-- 2. 指定注册中心地址 --&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>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">&amp;lt;!-- 3. 声明 GreetingService 的远程代理,并在 Dubbo 的 filter chain 中增加 tracing filter --&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;greetingService&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.api.GreetingService&amp;#34;&lt;/span> filter=&lt;span style="color:#2aa198">&amp;#34;tracing&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;!-- 4. zipkin 相关的配置 --&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;sender&amp;#34;&lt;/span> class=&lt;span style="color:#2aa198">&amp;#34;zipkin2.reporter.beans.OkHttpSenderFactoryBean&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;property&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;endpoint&amp;#34;&lt;/span> value=&lt;span style="color:#2aa198">&amp;#34;http://localhost:9411/api/v2/spans&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&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;tracing&amp;#34;&lt;/span> class=&lt;span style="color:#2aa198">&amp;#34;brave.spring.beans.TracingFactoryBean&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;property&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;localServiceName&amp;#34;&lt;/span> value=&lt;span style="color:#2aa198">&amp;#34;client&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;property&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;spanReporter&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> class=&lt;span style="color:#2aa198">&amp;#34;zipkin2.reporter.beans.AsyncReporterFactoryBean&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;property&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;sender&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;sender&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;!-- wait up to half a second for any in-flight spans on close --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;property&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;closeTimeout&amp;#34;&lt;/span> value=&lt;span style="color:#2aa198">&amp;#34;500&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&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/property&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;property&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;currentTraceContext&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> class=&lt;span style="color:#2aa198">&amp;#34;brave.spring.beans.CurrentTraceContextFactoryBean&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;property&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;scopeDecorators&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> class=&lt;span style="color:#2aa198">&amp;#34;brave.context.slf4j.MDCScopeDecorator&amp;#34;&lt;/span> factory-method=&lt;span style="color:#2aa198">&amp;#34;create&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;/property&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/bean&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/property&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/bean&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;/li>
&lt;/ol>
&lt;p>完成之后的工程的目录结构如下:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/zipkin-dubbo-project.png" alt="zipkin dubbo project structure">&lt;/p>
&lt;h3 id="运行">运行&lt;/h3>
&lt;p>现在让我们把整个链路运行起来,看看 Zipkin 链路追踪的效果。&lt;/p>
&lt;h4 id="启动-zookeeper-server">启动 Zookeeper Server&lt;/h4>
&lt;p>执行以下命令在本地启动一个 Zookeeper Server,如果没有安装,请自行从 &lt;a href="https://zookeeper.apache.org">ZooKeeper 官网&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-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>$ zkServer start
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="启动-zipkin-server">启动 Zipkin Server&lt;/h4>
&lt;p>执行以下命令在本地启动一个 Zipkin Server:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>$ curl -sSL https://zipkin.io/quickstart.sh | bash -s
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$ java -jar zipkin.jar
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="启动-helloservice">启动 HelloService&lt;/h4>
&lt;p>使用下面的命令启动 HelloService,当然也可以直接在 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>$ mvn exec:java -Dexec.mainClass&lt;span style="color:#719e07">=&lt;/span>com.alibaba.dubbo.samples.service.hello.Application
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>启动成功后应该可以在终端上看到 “Hello service started” 的字样。&lt;/p>
&lt;h4 id="启动-greetingservice">启动 GreetingService&lt;/h4>
&lt;p>使用下面的命令启动 GreetingService,当然也可以直接在 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>$ mvn exec:java -Dexec.mainClass&lt;span style="color:#719e07">=&lt;/span>com.alibaba.dubbo.samples.service.greeting.Application
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>启动成功后应该可以在终端上看到 “Greeting service started” 的字样。&lt;/p>
&lt;h4 id="运行-dubbo-客户端">运行 Dubbo 客户端&lt;/h4>
&lt;p>使用下面的命令运行 Dubbo 客户端向 GreetingService 发起远程调用,当然也可以直接在 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>$ mvn exec:java -Dexec.mainClass&lt;span style="color:#719e07">=&lt;/span>com.alibaba.dubbo.samples.client.Application
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>执行成功后,客户端会在终端上输出 “greeting, hello, world”。&lt;/p>
&lt;h4 id="链路追踪">链路追踪&lt;/h4>
&lt;p>打开浏览器访问 &amp;ldquo;http://localhost:9411&amp;rdquo; 并通过 &amp;ldquo;Find Traces&amp;rdquo; 按钮来搜索,可以找到刚刚调用的链路追踪,效果如下图所示:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/zipkin-trace.png" alt="zipkin trace">&lt;/p>
&lt;p>还可以进一步的选择每一个 span 来查看本次调用边界内的详情,比如,hello-service 这个 span 的详情如下:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/zipkin-span.png" alt="zipkin span">&lt;/p>
&lt;h2 id="总结">总结&lt;/h2>
&lt;p>本文介绍了链路追踪的基本概念以及 Zipkin 的基本用法,然后用 Dubbo 构建了一条最简单的调用链路,并引入了 Zipkin 做全链路追踪。由于 Zipkin 对 Dubbo 做了很好的支持,整个集成的过程还是十分简单明了的。&lt;/p>
&lt;p>Zipkin 对 Dubbo 的支持是构建在 Dubbo 的 filter 扩展机制上的,有兴趣的读者可以通过 &lt;a href="https://github.com/openzipkin/brave/blob/master/instrumentation/dubbo-rpc/src/main/java/brave/dubbo/rpc/TracingFilter.java">https://github.com/openzipkin/brave/blob/master/instrumentation/dubbo-rpc/src/main/java/brave/dubbo/rpc/TracingFilter.java&lt;/a> 了解其实现细节。&lt;/p>
&lt;p>本文中涉及的例子可以从 &lt;a href="https://github.com/dubbo/dubbo-samples">https://github.com/dubbo/dubbo-samples&lt;/a> 中的 &amp;ldquo;dubbo-samples-zipkin&amp;rdquo; 子模块中获取。另外,spring-cloud-sleth 2.0 中开始 &lt;a href="https://cloud.spring.io/spring-cloud-static/spring-cloud-sleuth/2.0.2.RELEASE/single/spring-cloud-sleuth.html#_dubbo_rpc_support">正式支持 Dubbo&lt;/a>,相关的文章和例子后续计划提供。&lt;/p></description></item></channel></rss>