| <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%BD%91%E5%85%B3/</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%BD%91%E5%85%B3/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> |
| <h1 id="higress-对接-dubbo-服务">Higress 对接 Dubbo 服务</h1> |
| <p>Higress提供了从HTTP协议到Dubbo协议进行转换的功能,用户通过配置协议转换,可以将一个Dubbo服务以HTTP接口暴露出来,从而用HTTP请求实现对Dubbo接口的调用。本文将通过一个示例来介绍如何用Higress配置HTTP到Dubbo的协议转换。该示例会引导您轻松地部署一个Nacos server和一个Dubbo服务,然后通过Ingress将HTTP请求转发到注册在Nacos上的Dubbo服务,并通过Higress的协议转换能力完成对Dubbo服务的HTTP调用。</p> |
| <h2 id="前提条件">前提条件</h2> |
| <ol> |
| <li>Higress目前支持的Dubbo框架的版本为2.x。若您使用Dubbo3.0,要求使用dubbo协议(目前暂不支持Triple协议)。</li> |
| <li>已安装Higress,并开启了对Istio CRD的支持,参考<a href="https://higress.io/zh-cn/docs/ops/deploy-by-helm">Higress安装部署文档</a>。</li> |
| </ol> |
| <h2 id="部署nacos和dubbo服务">部署Nacos和Dubbo服务</h2> |
| <p>首先在K8s集群中apply以下资源,以部署一个Nacos注册中心,同时通过K8s service将这个Nacos server暴露出来。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#586e75"># Nacos Server配置</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">apiVersion</span>: apps/v1 |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">kind</span>: Deployment |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">metadata</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">name</span>: nacos-server |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">spec</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">replicas</span>: <span style="color:#2aa198">1</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">selector</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">matchLabels</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">app</span>: nacos-server |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">template</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">metadata</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">labels</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">app</span>: nacos-server |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">spec</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">containers</span>: |
| </span></span><span style="display:flex;"><span> - <span style="color:#268bd2">env</span>: |
| </span></span><span style="display:flex;"><span> - <span style="color:#268bd2">name</span>: MODE |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">value</span>: standalone |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">image</span>: nacos/nacos-server:v2.2.0 |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">imagePullPolicy</span>: Always |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">name</span>: nacos-server |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">ports</span>: |
| </span></span><span style="display:flex;"><span> - <span style="color:#268bd2">containerPort</span>: <span style="color:#2aa198">8848</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">name</span>: server |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">dnsPolicy</span>: ClusterFirst |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">restartPolicy</span>: Always |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75"># Nacos Server Service配置</span> |
| </span></span><span style="display:flex;"><span>--- |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">apiVersion</span>: v1 |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">kind</span>: Service |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">metadata</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">name</span>: nacos-server |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">spec</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">ports</span>: |
| </span></span><span style="display:flex;"><span> - <span style="color:#268bd2">port</span>: <span style="color:#2aa198">8848</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">name</span>: server |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protocol</span>: TCP |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">targetPort</span>: <span style="color:#2aa198">8848</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">selector</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">app</span>: nacos-server |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">type</span>: ClusterIP |
| </span></span></code></pre></div><p>在K8s集群中apply以下资源,以部署一个Dubbo服务,该Dubbo服务将注册到上述的Naocs中。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#268bd2">apiVersion</span>: apps/v1 |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">kind</span>: Deployment |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">metadata</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">name</span>: nacos-provider |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">spec</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">replicas</span>: <span style="color:#2aa198">1</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">selector</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">matchLabels</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">app</span>: nacos-provider |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">template</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">metadata</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">labels</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">app</span>: nacos-provider |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">spec</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">containers</span>: |
| </span></span><span style="display:flex;"><span> - <span style="color:#268bd2">name</span>: server |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">image</span>: higress-registry.cn-hangzhou.cr.aliyuncs.com/samples/nacos-dubbo-provider:v1.0.0 |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">imagePullPolicy</span>: IfNotPresent |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">ports</span>: |
| </span></span><span style="display:flex;"><span> - <span style="color:#268bd2">containerPort</span>: <span style="color:#2aa198">20880</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">env</span>: |
| </span></span><span style="display:flex;"><span> - <span style="color:#268bd2">name</span>: DUBBO_REGISTRY_ADDRESS |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">value</span>: nacos-server.default.svc.cluster.local |
| </span></span></code></pre></div><p>该Dubbo服务的代码可以在Nacos的<a href="https://github.com/nacos-group/nacos-examples/tree/master/nacos-dubbo-example">示例代码</a>仓库中找到,其接口定义为:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">package</span> com.alibaba.nacos.example.dubbo.service; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">DemoService</span> { |
| </span></span><span style="display:flex;"><span> String <span style="color:#268bd2">sayName</span>(String name); |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>接口实现如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">package</span> com.alibaba.nacos.example.dubbo.service; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> com.alibaba.dubbo.config.annotation.Service; |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> com.alibaba.dubbo.rpc.RpcContext; |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.springframework.beans.factory.annotation.Value; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Default {@link DemoService} |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * https://nacos.io/zh-cn/docs/use-nacos-with-dubbo.html |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @since 2.6.5 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">@Service</span>(version <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;${demo.service.version}&#34;</span>, group <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;${demo.service.group}&#34;</span>) |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">DefaultService</span> <span style="color:#268bd2">implements</span> DemoService { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Value</span>(<span style="color:#2aa198">&#34;${demo.service.name}&#34;</span>) |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> String serviceName; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> String <span style="color:#268bd2">sayName</span>(String name) { |
| </span></span><span style="display:flex;"><span> RpcContext rpcContext <span style="color:#719e07">=</span> RpcContext.getContext(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> String.format(<span style="color:#2aa198">&#34;Service [name :%s , port : %d] %s(\&#34;%s\&#34;) : Hello,%s&#34;</span>, |
| </span></span><span style="display:flex;"><span> serviceName, |
| </span></span><span style="display:flex;"><span> rpcContext.getLocalPort(), |
| </span></span><span style="display:flex;"><span> rpcContext.getMethodName(), |
| </span></span><span style="display:flex;"><span> name, |
| </span></span><span style="display:flex;"><span> name); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>在本示例中,该Dubbo服务的服务名为com.alibaba.nacos.example.dubbo.service.DemoService,服务版本为1.0.0,服务分组为dev。</p> |
| <p>为了测试方便,我们可以通过运行以下命令来将我们部署在K8s集群中的Naocs服务映射到本地端口:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>kubectl port-forward svc/nacos-server 8848:8848 --address<span style="color:#719e07">=</span><span style="color:#2aa198">&#39;0.0.0.0&#39;</span> |
| </span></span></code></pre></div><p>然后请求Nacos的服务发现接口,可以查看到我们Dubbo服务的元数据信息,从而对以上部署进行验证。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#268bd2">$curl</span> -X GET <span style="color:#2aa198">&#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&#39;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">{</span><span style="color:#2aa198">&#34;name&#34;</span>:<span style="color:#2aa198">&#34;DEFAULT_GROUP@@providers:com.alibaba.nacos.example.dubbo.service.DemoService:1.0.0:dev&#34;</span>,<span style="color:#2aa198">&#34;groupName&#34;</span>:<span style="color:#2aa198">&#34;DEFAULT_GROUP&#34;</span>,<span style="color:#2aa198">&#34;clusters&#34;</span>:<span style="color:#2aa198">&#34;&#34;</span>,<span style="color:#2aa198">&#34;cacheMillis&#34;</span>:10000,<span style="color:#2aa198">&#34;hosts&#34;</span>:<span style="color:#719e07">[{</span><span style="color:#2aa198">&#34;ip&#34;</span>:<span style="color:#2aa198">&#34;10.244.0.58&#34;</span>,<span style="color:#2aa198">&#34;port&#34;</span>:20880,<span style="color:#2aa198">&#34;weight&#34;</span>:1.0,<span style="color:#2aa198">&#34;healthy&#34;</span>:true,<span style="color:#2aa198">&#34;enabled&#34;</span>:true,<span style="color:#2aa198">&#34;ephemeral&#34;</span>:true,<span style="color:#2aa198">&#34;clusterName&#34;</span>:<span style="color:#2aa198">&#34;DEFAULT&#34;</span>,<span style="color:#2aa198">&#34;serviceName&#34;</span>:<span style="color:#2aa198">&#34;DEFAULT_GROUP@@providers:com.alibaba.nacos.example.dubbo.service.DemoService:1.0.0:dev&#34;</span>,<span style="color:#2aa198">&#34;metadata&#34;</span>:<span style="color:#719e07">{</span><span style="color:#2aa198">&#34;side&#34;</span>:<span style="color:#2aa198">&#34;provider&#34;</span>,<span style="color:#2aa198">&#34;release&#34;</span>:<span style="color:#2aa198">&#34;dubbo_demo&#34;</span>,<span style="color:#2aa198">&#34;methods&#34;</span>:<span style="color:#2aa198">&#34;sayName&#34;</span>,<span style="color:#2aa198">&#34;deprecated&#34;</span>:<span style="color:#2aa198">&#34;false&#34;</span>,<span style="color:#2aa198">&#34;dubbo&#34;</span>:<span style="color:#2aa198">&#34;2.0.2&#34;</span>,<span style="color:#2aa198">&#34;pid&#34;</span>:<span style="color:#2aa198">&#34;3034042&#34;</span>,<span style="color:#2aa198">&#34;interface&#34;</span>:<span style="color:#2aa198">&#34;com.alibaba.nacos.example.dubbo.service.DemoService&#34;</span>,<span style="color:#2aa198">&#34;service-name-mapping&#34;</span>:<span style="color:#2aa198">&#34;true&#34;</span>,<span style="color:#2aa198">&#34;version&#34;</span>:<span style="color:#2aa198">&#34;1.0.0&#34;</span>,<span style="color:#2aa198">&#34;generic&#34;</span>:<span style="color:#2aa198">&#34;false&#34;</span>,<span style="color:#2aa198">&#34;revision&#34;</span>:<span style="color:#2aa198">&#34;dubbo_demo&#34;</span>,<span style="color:#2aa198">&#34;path&#34;</span>:<span style="color:#2aa198">&#34;com.alibaba.nacos.example.dubbo.service.DemoService&#34;</span>,<span style="color:#2aa198">&#34;protocol&#34;</span>:<span style="color:#2aa198">&#34;dubbo&#34;</span>,<span style="color:#2aa198">&#34;metadata-type&#34;</span>:<span style="color:#2aa198">&#34;remote&#34;</span>,<span style="color:#2aa198">&#34;application&#34;</span>:<span style="color:#2aa198">&#34;dubbo-provider-demo&#34;</span>,<span style="color:#2aa198">&#34;background&#34;</span>:<span style="color:#2aa198">&#34;false&#34;</span>,<span style="color:#2aa198">&#34;dynamic&#34;</span>:<span style="color:#2aa198">&#34;true&#34;</span>,<span style="color:#2aa198">&#34;category&#34;</span>:<span style="color:#2aa198">&#34;providers&#34;</span>,<span style="color:#2aa198">&#34;group&#34;</span>:<span style="color:#2aa198">&#34;dev&#34;</span>,<span style="color:#2aa198">&#34;anyhost&#34;</span>:<span style="color:#2aa198">&#34;true&#34;</span>,<span style="color:#2aa198">&#34;timestamp&#34;</span>:<span style="color:#2aa198">&#34;1680176973875&#34;</span><span style="color:#719e07">}</span>,<span style="color:#2aa198">&#34;ipDeleteTimeout&#34;</span>:30000,<span style="color:#2aa198">&#34;instanceHeartBeatInterval&#34;</span>:5000,<span style="color:#2aa198">&#34;instanceHeartBeatTimeOut&#34;</span>:15000<span style="color:#719e07">}]</span>,<span style="color:#2aa198">&#34;lastRefTime&#34;</span>:1680178336936,<span style="color:#2aa198">&#34;checksum&#34;</span>:<span style="color:#2aa198">&#34;&#34;</span>,<span style="color:#2aa198">&#34;allIPs&#34;</span>:false,<span style="color:#2aa198">&#34;reachProtectionThreshold&#34;</span>:false,<span style="color:#2aa198">&#34;valid&#34;</span>:true<span style="color:#719e07">}</span>% |
| </span></span></code></pre></div><h2 id="通过ingress转发请求到dubbo服务">通过Ingress转发请求到Dubbo服务</h2> |
| <p>Higress可以通过McpBridge来对接Nacos作为服务来源,在K8s集群中apply以下资源来配置McpBridge</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#268bd2">apiVersion</span>: networking.higress.io/v1 |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">kind</span>: McpBridge |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">metadata</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">name</span>: default |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">namespace</span>: higress-system |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">spec</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">registries</span>: |
| </span></span><span style="display:flex;"><span> - <span style="color:#268bd2">domain</span>: nacos-server.default.svc.cluster.local |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">nacosGroups</span>: |
| </span></span><span style="display:flex;"><span> - DEFAULT_GROUP |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">name</span>: nacos-service-resource |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">port</span>: <span style="color:#2aa198">8848</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">type</span>: nacos2 |
| </span></span></code></pre></div><p>通过McpBridge,我们可以直接从Nacos中发现Dubbo服务,并为其创建路由,而无需为每一个Dubbo服务创建service资源。</p> |
| <p>接下来我们创建如下Ingress,从而创建一条指向Dubbo服务的HTTP路由:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#268bd2">apiVersion</span>: networking.k8s.io/v1 |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">kind</span>: Ingress |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">metadata</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">annotations</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">higress.io/destination</span>: providers:com.alibaba.nacos.example.dubbo.service.DemoService:1.0.0:dev.DEFAULT-GROUP.public.nacos |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">name</span>: demo |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">namespace</span>: higress-system |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">spec</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">ingressClassName</span>: higress |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">rules</span>: |
| </span></span><span style="display:flex;"><span> - <span style="color:#268bd2">http</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">paths</span>: |
| </span></span><span style="display:flex;"><span> - <span style="color:#268bd2">backend</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">resource</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">apiGroup</span>: networking.higress.io |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">kind</span>: McpBridge |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">name</span>: default |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">path</span>: /dubbo |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">pathType</span>: Prefix |
| </span></span></code></pre></div><p>这样,path前缀为/dubbo的请求就会被路由到我们刚刚创建的Dubbo服务上。</p> |
| <h2 id="通过envoyfilter配置http到dubbo的协议转换规则">通过EnvoyFilter配置HTTP到Dubbo的协议转换规则</h2> |
| <p>经过上述步骤,我们已经在K8s环境下部署了一套Naocs和Dubbo,并通过Ingress将path前缀为/dubbo的请求路由到我们配好的Dubbo服务上。但光是这样是无法正常通信的,因为Dubbo服务使用的是定制的Dubbo协议,无法天然与HTTP协议进行兼容。因此接下来我们将通过EnvoyFilter来配置HTTP到Dubbo的协议转换规则,从而实现用HTTP请求来调用Dubbo服务。</p> |
| <p>在K8s集群中apply以下资源,要注意的是,EnvoyFilter是属于Istio的CRD,因此需要参照前提条件中的第2点来开启Higress对Istio CRD的支持。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#268bd2">apiVersion</span>: networking.istio.io/v1alpha3 |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">kind</span>: EnvoyFilter |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">metadata</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">name</span>: http-dubbo-transcoder-test |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">namespace</span>: higress-system |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">spec</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">configPatches</span>: |
| </span></span><span style="display:flex;"><span> - <span style="color:#268bd2">applyTo</span>: HTTP_FILTER |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">match</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">context</span>: GATEWAY |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">listener</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">filterChain</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">filter</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">name</span>: envoy.filters.network.http_connection_manager |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">subFilter</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">name</span>: envoy.filters.http.router |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">patch</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">operation</span>: INSERT_BEFORE |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">value</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">name</span>: envoy.filters.http.http_dubbo_transcoder |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">typed_config</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#39;@type&#39;</span>: type.googleapis.com/udpa.type.v1.TypedStruct |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">type_url</span>: type.googleapis.com/envoy.extensions.filters.http.http_dubbo_transcoder.v3.HttpDubboTranscoder |
| </span></span><span style="display:flex;"><span> - <span style="color:#268bd2">applyTo</span>: HTTP_ROUTE |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">match</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">context</span>: GATEWAY |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">routeConfiguration</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">vhost</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">route</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">name</span>: demo |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">patch</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">operation</span>: MERGE |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">value</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">route</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">upgrade_configs</span>: |
| </span></span><span style="display:flex;"><span> - <span style="color:#268bd2">connect_config</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">allow_post</span>: <span style="color:#cb4b16">true</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">upgrade_type</span>: CONNECT |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">typed_per_filter_config</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">envoy.filters.http.http_dubbo_transcoder</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#39;@type&#39;</span>: type.googleapis.com/udpa.type.v1.TypedStruct |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">type_url</span>: type.googleapis.com/envoy.extensions.filters.http.http_dubbo_transcoder.v3.HttpDubboTranscoder |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">value</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">request_validation_options</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">reject_unknown_method</span>: <span style="color:#cb4b16">true</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">reject_unknown_query_parameters</span>: <span style="color:#cb4b16">true</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">services_mapping</span>: |
| </span></span><span style="display:flex;"><span> - <span style="color:#268bd2">group</span>: dev |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">method_mapping</span>: |
| </span></span><span style="display:flex;"><span> - <span style="color:#268bd2">name</span>: sayName |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">parameter_mapping</span>: |
| </span></span><span style="display:flex;"><span> - <span style="color:#268bd2">extract_key</span>: p |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">extract_key_spec</span>: ALL_QUERY_PARAMETER |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">mapping_type</span>: java.lang.String |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">passthrough_setting</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">passthrough_all_headers</span>: <span style="color:#cb4b16">true</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">path_matcher</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">match_http_method_spec</span>: ALL_GET |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">match_pattern</span>: /dubbo/hello |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">name</span>: com.alibaba.nacos.example.dubbo.service.DemoService |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">version</span>: <span style="color:#2aa198">1.0.0</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">url_unescape_spec</span>: ALL_CHARACTERS_EXCEPT_RESERVED |
| </span></span><span style="display:flex;"><span> - <span style="color:#268bd2">applyTo</span>: CLUSTER |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">match</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">cluster</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">service</span>: providers:com.alibaba.nacos.example.dubbo.service.DemoService:1.0.0:dev.DEFAULT-GROUP.public.nacos |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">context</span>: GATEWAY |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">patch</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">operation</span>: MERGE |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">value</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">upstream_config</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">name</span>: envoy.upstreams.http.dubbo_tcp |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">typed_config</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#39;@type&#39;</span>: type.googleapis.com/udpa.type.v1.TypedStruct |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">type_url</span>: type.googleapis.com/envoy.extensions.upstreams.http.dubbo_tcp.v3.DubboTcpConnectionPoolProto |
| </span></span></code></pre></div><p>在以上EnvoyFilter中,我们配置了将path为/dubbo/hello的HTTP请求转发到Dubbo服务com.alibaba.nacos.example.dubbo.service.DemoService:1.0.0:dev中,并调用其sayName方法,而该方法的参数则通过HTTP url中的的query参数p来指定。</p> |
| <h2 id="请求验证">请求验证</h2> |
| <p>通过以上配置,我们就可以执行以下curl命令来调用这个dubbo服务了:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#268bd2">$curl</span> <span style="color:#2aa198">&#34;localhost/dubbo/hello?p=abc&#34;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">{</span><span style="color:#2aa198">&#34;result&#34;</span>:<span style="color:#2aa198">&#34;Service [name :demoService , port : 20880] sayName(\&#34;abc\&#34;) : Hello,abc&#34;</span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="配置参考">配置参考</h2> |
| <p>EnvoyFilter的相关配置项参考<a href="https://higress.io/zh-cn/docs/user/dubbo-envoyfilter">HTTP转Dubbo配置说明</a></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> |
| <p><img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/ApacheShenYu-Dubbo-zh.png" alt="img"></p> |
| <h2 id="1-介绍">1. 介绍</h2> |
| <ul> |
| <li>Apache ShenYu</li> |
| </ul> |
| <p><img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/shenyu.png" alt="img"></p> |
| <p><a href="https://shenyu.apache.org/zh/docs/index">Apache ShenYu(Incubating)</a> 是一个异步的,高性能的,跨语言的,响应式的 <code>API</code> 网关。兼容各种主流框架体系,支持热插拔,用户可以定制化开发,满足用户各种场景的现状和未来需求,经历过大规模场景的锤炼。</p> |
| <p>2021年5月,<code>ShenYu</code>捐献给 <code>Apache</code> 软件基金会,Apache 基金会全票通过,顺利进入孵化器。</p> |
| <ul> |
| <li>Apache Dubbo</li> |
| </ul> |
| <p><code>Apache Dubbo</code> 是一款微服务开发框架,它提供了 <code>RPC</code> 通信 与 微服务治理 两大关键能力。这意味着,使用 <code>Dubbo</code> 开发的微服务,将具备相互之间的远程发现与通信能力, 同时利用 Dubbo 提供的丰富服务治理能力,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。同时 <code>Dubbo</code> 是高度可扩展的,用户几乎可以在任意功能点去定制自己的实现,以改变框架的默认行为来满足自己的业务需求。</p> |
| <h2 id="2-dubbo快速开始">2. Dubbo快速开始</h2> |
| <p>本小节介绍如何将<code>Dubbo</code>服务接入到<code>ShenYu</code>网关,您可以直接在工程下找到本小节的<a href="https://github.com/apache/shenyu/tree/master/shenyu-examples/shenyu-examples-dubbo">示例代码</a> 。</p> |
| <h3 id="21-启动shenyu-admin">2.1 启动shenyu-admin</h3> |
| <p><code>shenyu-admin</code>是<code>Apache ShenYu</code>后台管理系统, 启动的方式有多种,本文通过 <code>[本地部署](https://shenyu.apache.org/zh/docs/deployment/deployment-local)</code> 的方式启动。启动成功后,需要在基础配置<code>-&gt;</code>插件管理中,把<code>dubbo</code> 插件设置为开启,并设置你的注册地址,请确保注册中心已经开启。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/dubbo-enable-zh.png" alt="img"></p> |
| <h3 id="22-启动shenyu网关">2.2 启动shenyu网关</h3> |
| <p>在这里通过 <a href="https://github.com/apache/incubator-shenyu/tree/master/shenyu-bootstrap">源码</a> 的方式启动,直接运行<code>shenyu-bootstrap</code>中的<code>ShenyuBootstrapApplication</code>。</p> |
| <p>在启动前,请确保网关已经引入相关依赖。如果客户端是<code>apache dubbo</code>,注册中心使用<code>zookeeper</code>,请参考如下配置:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#719e07">&lt;!--</span> apache shenyu apache dubbo plugin start<span style="color:#719e07">--&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>dependency<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>groupId<span style="color:#719e07">&gt;</span>org.apache.shenyu<span style="color:#719e07">&lt;/</span>groupId<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>artifactId<span style="color:#719e07">&gt;</span>shenyu<span style="color:#719e07">-</span>spring<span style="color:#719e07">-</span>boot<span style="color:#719e07">-</span>starter<span style="color:#719e07">-</span>plugin<span style="color:#719e07">-</span>apache<span style="color:#719e07">-</span>dubbo<span style="color:#719e07">&lt;/</span>artifactId<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>version<span style="color:#719e07">&gt;</span>${project.version}<span style="color:#719e07">&lt;/</span>version<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;/</span>dependency<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>dependency<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>groupId<span style="color:#719e07">&gt;</span>org.apache.dubbo<span style="color:#719e07">&lt;/</span>groupId<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>artifactId<span style="color:#719e07">&gt;</span>dubbo<span style="color:#719e07">&lt;/</span>artifactId<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>version<span style="color:#719e07">&gt;</span>2.7.5<span style="color:#719e07">&lt;/</span>version<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;/</span>dependency<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;!--</span> Dubbo zookeeper registry dependency start <span style="color:#719e07">--&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>dependency<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>groupId<span style="color:#719e07">&gt;</span>org.apache.curator<span style="color:#719e07">&lt;/</span>groupId<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>artifactId<span style="color:#719e07">&gt;</span>curator<span style="color:#719e07">-</span>client<span style="color:#719e07">&lt;/</span>artifactId<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>version<span style="color:#719e07">&gt;</span>4.0.1<span style="color:#719e07">&lt;/</span>version<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>exclusions<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>exclusion<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>artifactId<span style="color:#719e07">&gt;</span>log4j<span style="color:#719e07">&lt;/</span>artifactId<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>groupId<span style="color:#719e07">&gt;</span>log4j<span style="color:#719e07">&lt;/</span>groupId<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;/</span>exclusion<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;/</span>exclusions<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;/</span>dependency<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>dependency<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>groupId<span style="color:#719e07">&gt;</span>org.apache.curator<span style="color:#719e07">&lt;/</span>groupId<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>artifactId<span style="color:#719e07">&gt;</span>curator<span style="color:#719e07">-</span>framework<span style="color:#719e07">&lt;/</span>artifactId<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>version<span style="color:#719e07">&gt;</span>4.0.1<span style="color:#719e07">&lt;/</span>version<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;/</span>dependency<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>dependency<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>groupId<span style="color:#719e07">&gt;</span>org.apache.curator<span style="color:#719e07">&lt;/</span>groupId<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>artifactId<span style="color:#719e07">&gt;</span>curator<span style="color:#719e07">-</span>recipes<span style="color:#719e07">&lt;/</span>artifactId<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>version<span style="color:#719e07">&gt;</span>4.0.1<span style="color:#719e07">&lt;/</span>version<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;/</span>dependency<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;!--</span> Dubbo zookeeper registry dependency end <span style="color:#719e07">--&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;!--</span> apache dubbo plugin end<span style="color:#719e07">--&gt;</span> |
| </span></span></code></pre></div><h3 id="23-启动shenyu-examples-dubbo">2.3 启动shenyu-examples-dubbo</h3> |
| <p>以官网提供的例子为例 <a href="https://github.com/apache/shenyu/tree/master/shenyu-examples/shenyu-examples-dubbo">shenyu-examples-dubbo</a> 。 假如<code>dubbo</code>服务定义如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;beans</span> /* ...... * <span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dubbo:application</span> name=<span style="color:#2aa198">&#34;test-dubbo-service&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dubbo:registry</span> address=<span style="color:#2aa198">&#34;${dubbo.registry.address}&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dubbo:protocol</span> name=<span style="color:#2aa198">&#34;dubbo&#34;</span> port=<span style="color:#2aa198">&#34;20888&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dubbo:service</span> timeout=<span style="color:#2aa198">&#34;10000&#34;</span> interface=<span style="color:#2aa198">&#34;org.apache.shenyu.examples.dubbo.api.service.DubboTestService&#34;</span> ref=<span style="color:#2aa198">&#34;dubboTestService&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;/beans&gt;</span> |
| </span></span></code></pre></div><p>声明应用服务名称,注册中心地址,使用<code>dubbo</code>协议,声明服务接口,对应接口实现类:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * DubboTestServiceImpl. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">@Service</span>(<span style="color:#2aa198">&#34;dubboTestService&#34;</span>) |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">DubboTestServiceImpl</span> <span style="color:#268bd2">implements</span> DubboTestService { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@ShenyuDubboClient</span>(path <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;/findById&#34;</span>, desc <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;Query by Id&#34;</span>) |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> DubboTest <span style="color:#268bd2">findById</span>(<span style="color:#268bd2">final</span> String id) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> DubboTest(id, <span style="color:#2aa198">&#34;hello world shenyu Apache, findById&#34;</span>); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//......</span> |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>在接口实现类中,使用注解<code>@ShenyuDubboClient</code>向<code>shenyu-admin</code>注册服务。</p> |
| <p>在配置文件<code>application.yml</code>中的配置信息:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#268bd2">server</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">port</span>: <span style="color:#2aa198">8011</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">address</span>: <span style="color:#2aa198">0.0.0.0</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">servlet</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">context-path</span>: / |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">spring</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">main</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">allow-bean-definition-overriding</span>: <span style="color:#cb4b16">true</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">dubbo</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">registry</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">address</span>: zookeeper://localhost:2181 <span style="color:#586e75"># dubbo使用的注册中心</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">shenyu</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">register</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">registerType</span>: http <span style="color:#586e75">#注册方式</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">serverLists</span>: http://localhost:9095 <span style="color:#586e75">#注册地址</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">props</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">username</span>: admin |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">password</span>: <span style="color:#2aa198">123456</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">client</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">dubbo</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">props</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">contextPath</span>: /dubbo |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">appName</span>: dubbo |
| </span></span></code></pre></div><p>在配置文件中,声明<code>dubbo</code>使用的注册中心地址,<code>dubbo</code>服务向<code>shenyu-admin</code>注册,使用的方式是<code>http</code>,注册地址是<code>http://localhost:9095</code>。</p> |
| <p>关于注册方式的使用,请参考 <code>[应用客户端接入](https://shenyu.apache.org/docs/design/register-center-design/)</code> 。</p> |
| <h3 id="24-调用dubbo服务">2.4 调用dubbo服务</h3> |
| <p><code>shenyu-examples-dubbo</code>项目成功启动之后会自动把加 <code>@ShenyuDubboClient</code> 注解的接口方法注册到网关。</p> |
| <p>打开 <code>插件列表 -&gt; Proxy -&gt; dubbo</code> 可以看到插件规则配置列表:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/dubbo-service-list-zh.png" alt="img"></p> |
| <p>注册成功的选择器信息:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/dubbo-selector-zh.png" alt="img"></p> |
| <p>注册成功的规则信息:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/dubbo-rule-zh.png" alt="img"></p> |
| <blockquote> |
| <p>选择器和规则是 <code>Apache ShenYu</code> 网关中最灵魂的东西。掌握好它,你可以对任何流量进行管理。对应为选择器与规则里面的匹配条件(conditions),根据不同的流量筛选规则,我们可以处理各种复杂的场景。流量筛选可以从<code>Header</code>, <code>URI</code>, <code>Query</code>, <code>Cookie</code> 等等Http请求获取数据。</p> |
| <p>然后可以采用 <code>Match</code>,<code>=</code>,<code>Regex</code>,<code>Groovy</code>,<code>Exclude</code>等匹配方式,匹配出你所预想的数据。多组匹配添加可以使用<code>And/Or</code>的匹配策略。</p> |
| <p>具体的介绍与使用请看: <code>[选择器与规则管理](https://shenyu.apache.org/zh/docs/user-guide/admin-usage/selector-and-rule)</code> 。</p> |
| </blockquote> |
| <p>发起<code>GET</code>请求,通过<code>ShenYu</code>网关调用<code>dubbo</code>服务:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>GET http://localhost:9195/dubbo/findById?id=100 |
| </span></span><span style="display:flex;"><span>Accept: application/json |
| </span></span></code></pre></div><p>成功响应之后,结果如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>{ |
| </span></span><span style="display:flex;"><span> &#34;name&#34;: &#34;hello world shenyu Apache, findById&#34;, |
| </span></span><span style="display:flex;"><span> &#34;id&#34;: &#34;100&#34; |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>至此,就成功的通过<code>http</code>请求访问<code>dubbo</code>服务了,<code>ShenYu</code>网关通过<code>shenyu-plugin-dubbo</code>模块将<code>http</code>协议转成了<code>dubbo</code>协议。</p> |
| <h2 id="3-深入理解dubbo插件">3. 深入理解Dubbo插件</h2> |
| <p>在运行上述<code>demo</code>的过程中,是否存在一些疑问:</p> |
| <ul> |
| <li><code>dubbo</code>服务是如何注册到<code>shenyu-admin</code>?</li> |
| <li><code>shenyu-admin</code>是如何将数据同步到<code>ShenYu</code>网关?</li> |
| <li><code>DubboPlugin</code>是如何将<code>http</code>协议转换到到dubbo协议?</li> |
| </ul> |
| <p>带着这些疑问,来深入理解<code>dubbo</code>插件。</p> |
| <h3 id="31-应用客户端接入">3.1 应用客户端接入</h3> |
| <p>应用客户端接入是指将微服务接入到<code>Apache ShenYu</code>网关,当前支持<code>Http</code>、 <code>Dubbo</code>、 <code>Spring Cloud</code>、 <code>gRPC</code>、 <code>Motan</code>、 <code>Sofa</code>、 <code>Tars</code>等协议的接入。</p> |
| <p>将应用客户端接入到<code>Apache ShenYu</code>网关是通过注册中心来实现的,涉及到客户端注册和服务端同步数据。注册中心支持<code>Http</code>、<code>Zookeeper</code>、<code>Etcd</code>、<code>Consul</code>和<code>Nacos</code>。默认是通过<code>Http</code>方式注册。</p> |
| <p>客户端接入的相关配置请参考 <code>[客户端接入配置](https://shenyu.apache.org/zh/docs/user-guide/register-center-access)</code> 。</p> |
| <h4 id="311-客户端注册">3.1.1 客户端注册</h4> |
| <p><img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/register-client.png" alt="img"></p> |
| <p>在你的微服务配置中声明注册中心客户端类型,如<code>Http</code>或<code>Zookeeper</code>。 |
| 应用程序启动时使用<code>SPI</code>方式加载并初始化对应注册中心客户端,通过实现<code>Spring Bean</code>相关的后置处理器接口,在其中获取需要进行注册的服务接口信息,将获取的信息放入<code>Disruptor</code>中。</p> |
| <p>注册中心客户端从<code>Disruptor</code>中读取数据,并将接口信息注册到<code>shenyu-admin</code>,<code>Disruptor</code>在其中起数据与操作解耦的作用,利于扩展。</p> |
| <h4 id="312-服务端注册">3.1.2 服务端注册</h4> |
| <p><img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/register-server.png" alt="img"></p> |
| <p>在<code>shenyu-admin</code>配置中声明注册中心服务端类型,如<code>Http</code>或<code>Zookeeper</code>。当<code>shenyu-admin</code>启动时,读取配置类型,加载并初始化对应的注册中心服务端,注册中心服务端收到<code>shenyu-client</code>注册的接口信息后,将其放入<code>Disruptor</code>中,然后会触发注册处理逻辑,将服务接口信息更新并发布同步事件。</p> |
| <p><code>Disruptor</code>在其中起到数据与操作解耦,利于扩展。如果注册请求过多,导致注册异常,也有数据缓冲作用。</p> |
| <h3 id="32-数据同步原理">3.2 数据同步原理</h3> |
| <p>数据同步是指在 <code>shenyu-admin</code> 后台操作数据以后,使用何种策略将数据同步到 <code>Apache ShenYu</code> 网关。<code>Apache ShenYu</code> 网关当前支持<code>ZooKeeper</code>、<code>WebSocket</code>、<code>Http长轮询</code>、<code>Nacos</code> 、<code>Etcd</code> 和 <code>Consul</code> 进行数据同步。默认是通过<code>WebSocket</code>进行数据同步。</p> |
| <p>数据同步的相关配置请参考 <code>[数据同步配置](https://shenyu.apache.org/zh/docs/user-guide/use-data-sync)</code> 。</p> |
| <h4 id="321-数据同步的意义">3.2.1 数据同步的意义</h4> |
| <p>网关是流量请求的入口,在微服务架构中承担了非常重要的角色,网关高可用的重要性不言而喻。在使用网关的过程中,为了满足业务诉求,经常需要变更配置,比如流控规则、路由规则等等。因此,网关动态配置是保障网关高可用的重要因素。</p> |
| <p>当前数据同步特性如下:</p> |
| <ul> |
| <li>所有的配置都缓存在 <code>Apache ShenYu</code> 网关内存中,每次请求都使用本地缓存,速度非常快。</li> |
| <li>用户可以在 <code>shenyu-admin</code> 后台任意修改数据,并马上同步到网关内存。</li> |
| <li>支持 <code>Apache ShenYu</code> 的插件、选择器、规则数据、元数据、签名数据等数据同步。</li> |
| <li>所有插件的选择器,规则都是动态配置,立即生效,不需要重启服务。</li> |
| <li>数据同步方式支持 <code>Zookeeper</code>、<code>Http 长轮询</code>、<code>Websocket</code>、<code>Nacos</code>、<code>Etcd</code> 和 <code>Consul</code>。</li> |
| </ul> |
| <h4 id="322-数据同步原理分析">3.2.2 数据同步原理分析</h4> |
| <p>下图展示了 <code>Apache ShenYu</code> 数据同步的流程,<code>Apache ShenYu</code> 网关在启动时,会从配置服务同步配置数据,并且支持推拉模式获取配置变更信息,然后更新本地缓存。管理员可以在管理后台(<code>shenyu-admin</code>),变更用户权限、规则、插件、流量配置,通过推拉模式将变更信息同步给 <code>Apache ShenYu</code> 网关,具体是 <code>push</code> 模式,还是 <code>pull</code> 模式取决于使用哪种同步方式。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/data-sync.png" alt="img"></p> |
| <p>在最初的版本中,配置服务依赖 <code>Zookeeper</code> 实现,管理后台将变更信息 <code>push</code> 给网关。而现在可以支持 <code>WebSocket</code>、<code>Http长轮询</code>、<code>Zookeeper</code>、<code>Nacos</code>、<code>Etcd</code> 和 <code>Consul</code>,通过在配置文件中设置 <code>shenyu.sync.${strategy}</code> 指定对应的同步策略,默认使用 <code>webosocket</code> 同步策略,可以做到秒级数据同步。但是,有一点需要注意的是,<code>Apache ShenYu</code>网关 和 <code>shenyu-admin</code> 必须使用相同的同步策略。</p> |
| <p>如下图所示,<code>shenyu-admin</code> 在用户发生配置变更之后,会通过 <code>EventPublisher</code> 发出配置变更通知,由 <code>EventDispatcher</code> 处理该变更通知,然后根据配置的同步策略(<code>http、weboscket、zookeeper、naocs、etcd、consul</code>),将配置发送给对应的事件处理器。</p> |
| <ul> |
| <li>如果是 <code>websocket</code> 同步策略,则将变更后的数据主动推送给 <code>shenyu-web</code>,并且在网关层,会有对应的 <code>WebsocketDataHandler</code> 处理器来处理 <code>shenyu-admin</code> 的数据推送。</li> |
| <li>如果是 <code>zookeeper</code> 同步策略,将变更数据更新到 <code>zookeeper</code>,而 <code>ZookeeperSyncCache</code> 会监听到 <code>zookeeper</code> 的数据变更,并予以处理。</li> |
| <li>如果是 <code>http</code> 同步策略,由网关主动发起长轮询请求,默认有 <code>90s</code> 超时时间,如果 <code>shenyu-admin</code> 没有数据变更,则会阻塞 <code>http</code> 请求,如果有数据发生变更则响应变更的数据信息,如果超过 <code>60s</code> 仍然没有数据变更则响应空数据,网关层接到响应后,继续发起 <code>http</code> 请求,反复同样的请求。</li> |
| </ul> |
| <h3 id="33-流程分析">3.3 流程分析</h3> |
| <p>流程分析是从源码的角度,展示服务注册流程,数据同步流程和服务调用流程。</p> |
| <h4 id="331-服务注册流程">3.3.1 服务注册流程</h4> |
| <ul> |
| <li>读取dubbo服务</li> |
| </ul> |
| <p>使用注解<code>@ShenyuDubboClient</code>标记需要注册到网关的<code>dubbo</code>服务。</p> |
| <p>注解扫描通过<code>ApacheDubboServiceBeanListener</code>完成,它实现了<code>ApplicationListener&lt;ContextRefreshedEvent&gt;</code>接口,在<code>Spring</code>容器启动过程中,发生上下文刷新事件时,开始执行事件处理方法<code>onApplicationEvent()</code>。在重写的方法逻辑中,读取<code>Dubbo</code>服务<code>ServiceBean</code>,构建元数据对象和<code>URI</code>对象,并向<code>shenyu-admin</code>注册。</p> |
| <p>具体的注册逻辑由注册中心实现,请参考 <code>[客户端接入原理](https://shenyu.apache.org/zh/docs/design/register-center-design/)</code> 。</p> |
| <ul> |
| <li>处理注册信息</li> |
| </ul> |
| <p>客户端通过注册中心注册的元数据和<code>URI</code>数据,在<code>shenyu-admin</code>端进行处理,负责存储到数据库和同步给<code>shenyu</code>网关。<code>Dubbo</code>插件的客户端注册处理逻辑在<code>ShenyuClientRegisterDubboServiceImpl</code>中。继承关系如下:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/ShenyuClientRegisterDubboServiceImpl.png" alt="img"></p> |
| <ul> |
| <li>ShenyuClientRegisterService:客户端注册服务,顶层接口;</li> |
| <li>FallbackShenyuClientRegisterService:注册失败,提供重试操作;</li> |
| <li>AbstractShenyuClientRegisterServiceImpl:抽象类,实现部分公共注册逻辑;</li> |
| <li>ShenyuClientRegisterDubboServiceImpl:实现<code>Dubbo</code>插件的注册;</li> |
| </ul> |
| <p>注册信息包括选择器,规则和元数据。</p> |
| <p>整体的<code>dubbo</code>服务注册流程如下:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/dubbo-register-zh.png" alt="img"></p> |
| <h4 id="332-数据同步流程">3.3.2 数据同步流程</h4> |
| <ul> |
| <li>admin更新数据</li> |
| </ul> |
| <p>假设在在后台管理系统中,新增一条选择器数据,请求会进入<code>SelectorController</code>类中的<code>createSelector()</code>方法,它负责数据的校验,添加或更新数据,返回结果信息。</p> |
| <p>在<code>SelectorServiceImpl</code>类中通过<code>createOrUpdate()</code>方法完成数据的转换,保存到数据库,发布事件,更新<code>upstream</code>。</p> |
| <p>在<code>Service</code>类完成数据的持久化操作,即保存数据到数据库。发布变更数据通过<code>eventPublisher.publishEvent()</code>完成,这个<code>eventPublisher</code>对象是一个<code>ApplicationEventPublisher</code>类,这个类的全限定名是<code>org.springframework.context.ApplicationEventPublisher</code>,发布数据的功能正是是通过<code>Spring</code>相关的功能来完成的。</p> |
| <p>当事件发布完成后,会自动进入到<code>DataChangedEventDispatcher</code>类中的<code>onApplicationEvent()</code>方法,根据不同数据类型和数据同步方式进行事件处理。</p> |
| <ul> |
| <li>网关数据同步</li> |
| </ul> |
| <p>网关在启动时,根据指定的数据同步方式加载不同的配置类,初始化数据同步相关类。</p> |
| <p>在接收到数据后,进行反序列化操作,读取数据类型和操作类型。不同的数据类型,有不同的数据处理方式,所以有不同的实现类。但是它们之间也有相同的处理逻辑,所以可以通过模板方法设计模式来实现。相同的逻辑放在抽象类<code>AbstractDataHandler</code>中的<code>handle()</code>方法中,不同逻辑就交给各自的实现类。</p> |
| <p>新增一条选择器数据,是新增操作,会进入到<code>SelectorDataHandler.doUpdate()</code>具体的数据处理逻辑中。</p> |
| <p>在通用插件数据订阅者<code>CommonPluginDataSubscriber</code>,负责处理所有插件、选择器和规则信息</p> |
| <p>将数据保存到网关的内存中,<code>BaseDataCache</code>是最终缓存数据的类,通过单例模式实现。选择器数据就存到了<code>SELECTOR_MAP</code>这个<code>Map</code>中。在后续使用的时候,也是从这里拿数据。</p> |
| <p>上述逻辑用流程图表示如下:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/data-sync-seq-zh.png" alt="img"></p> |
| <h4 id="333-服务调用流程">3.3.3 服务调用流程</h4> |
| <p>在<code>Dubbo</code>插件体系中,类继承关系如下:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/ApacheDubboPlugin.png" alt="img"></p> |
| <blockquote> |
| <p>ShenyuPlugin:顶层接口,定义接口方法;</p> |
| <p>AbstractShenyuPlugin:抽象类,实现插件共有逻辑;</p> |
| <p>AbstractDubboPlugin:dubbo插件抽象类,实现<code>dubbo</code>共有逻辑(ShenYu网关支持ApacheDubbo和AlibabaDubbo);</p> |
| <p>ApacheDubboPlugin:ApacheDubbo插件。</p> |
| </blockquote> |
| <ul> |
| <li>org.apache.shenyu.web.handler.ShenyuWebHandler.DefaultShenyuPluginChain#execute()</li> |
| </ul> |
| <p>通过<code>ShenYu</code>网关代理后,请求入口是<code>ShenyuWebHandler</code>,它实现了<code>org.springframework.web.server.WebHandler</code>接口,通过责任链设计模式将所有插件连接起来。</p> |
| <ul> |
| <li>org.apache.shenyu.plugin.base.AbstractShenyuPlugin#execute()</li> |
| </ul> |
| <p>当请求到网关时,判断某个插件是否执行,是通过指定的匹配逻辑来完成。在<code>execute()</code>方法中执行选择器和规则的匹配逻辑。</p> |
| <ul> |
| <li>org.apache.shenyu.plugin.global.GlobalPlugin#execute()</li> |
| </ul> |
| <p>最先被执行的是<code>GlobalPlugin</code> ,它是一个全局插件,在<code>execute()</code>方法中构建上下文信息。</p> |
| <ul> |
| <li>org.apache.shenyu.plugin.base.RpcParamTransformPlugin#execute()</li> |
| </ul> |
| <p>接着被执行的是<code>RpcParamTransformPlugin</code> , 它负责从<code>http</code>请求中读取参数,保存到<code>exchange</code>中,传递给<code>rpc</code>服务。在<code>execute()</code>方法中,执行该插件的核心逻辑:从<code>exchange</code>中获取请求信息,根据请求传入的内容形式处理参数。</p> |
| <ul> |
| <li>org.apache.shenyu.plugin.dubbo.common.AbstractDubboPlugin</li> |
| </ul> |
| <p>然后被执行的是<code>DubboPlugin</code> 。在<code>doExecute()</code>方法中,主要是检查元数据和参数。在<code>doDubboInvoker()</code>方法中设置特殊的上下文信息,然后开始<code>dubbo</code>的泛化调用。</p> |
| <p>在<code>genericInvoker()</code>方法中:</p> |
| <ul> |
| <li>获取<code>ReferenceConfig</code>对象;</li> |
| <li>获取泛化服务<code>GenericService</code>对象;</li> |
| <li>构造请求参数<code>pair</code>对象;</li> |
| <li>发起异步的泛化调用。</li> |
| </ul> |
| <p>通过泛化调用就可以实现在网关调用<code>dubbo</code>服务了。</p> |
| <p><code>ReferenceConfig</code>对象是支持泛化调用的关键对象 ,它的初始化操作是在数据同步的时候完成的。</p> |
| <ul> |
| <li>org.apache.shenyu.plugin.response.ResponsePlugin#execute()</li> |
| </ul> |
| <p>最后被执行的是<code>ResponsePlugin</code> ,它统一处理网关的响应结果信息。处理类型由<code>MessageWriter</code>决定,类继承关系如下:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/MessageWriter.png" alt="img"></p> |
| <blockquote> |
| <p>MessageWriter:接口,定义消息处理方法;</p> |
| <p>NettyClientMessageWriter:处理<code>Netty</code>调用结果;</p> |
| <p>RPCMessageWriter:处理<code>RPC</code>调用结果;</p> |
| <p>WebClientMessageWriter:处理<code>WebClient</code>调用结果;</p> |
| </blockquote> |
| <p><code>Dubbo</code>服务调用,处理结果是<code>RPCMessageWriter</code>。</p> |
| <ul> |
| <li>org.apache.shenyu.plugin.response.strategy.RPCMessageWriter#writeWith()</li> |
| </ul> |
| <p>在<code>writeWith()</code>方法中处理响应结果,获取结果或处理异常。</p> |
| <p>分析至此,关于<code>Dubbo</code>插件的源码分析就完成了,分析流程图如下:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/dubbo-execute-zh.png" alt="img"></p> |
| <h2 id="4-小结">4. 小结</h2> |
| <p>本文从实际案例出发,由浅入深分析了<code>ShenYu</code>网关对Dubbo服务的代理过程。涉及到的主要知识点如下:</p> |
| <ul> |
| <li>通过责任链设计模式执行插件;</li> |
| <li>使用模板方法设计模式实现<code>AbstractShenyuPlugin</code>,处理通用的操作类型;</li> |
| <li>使用单例设计模式实现缓存数据类<code>BaseDataCache</code>;</li> |
| <li>通过<code>springboot starter</code>即可引入不同的注册中心和数同步方式,扩展性很好;</li> |
| <li>通过<code>admin</code>支持规则热更新,方便流量管控;</li> |
| <li><code>Disruptor</code>队列是为了数据与操作解耦,以及数据缓冲。</li> |
| </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> |
| <h2 id="背景">背景</h2> |
| <p><a href="https://dubbo.apache.org/zh-cn/">Apache Dubbo</a> 是由阿里巴巴开源并捐赠给 Apache 的微服务开发框架,它提供了 RPC 通信与微服务治理两大关键能力。不仅经过了阿里电商场景中海量流量的验证,也在国内的技术公司中被广泛落地。</p> |
| <p>在实际应用场景中,Apache Dubbo 一般会作为后端系统间 RPC 调用的实现框架,当需要提供 HTTP 接口给到前端时,会通过一个「胶水层」将 Dubbo Service 包装成 HTTP 接口,再交付到前端系统。</p> |
| <p><a href="https://apisix.apache.org/">Apache APISIX</a> 是 Apache 软件基金会的顶级开源项目,也是当前最活跃的开源网关项目。作为一个动态、实时、高性能的开源 API 网关,Apache APISIX 提供了负载均衡、动态上游、灰度发布、服务熔断、身份认证、可观测性等丰富的流量管理功能。</p> |
| <p>得益于 Apache Dubbo 的应用场景优势,Apache APISIX 基于开源项目 tengine/mod_dubbo 模块为 Apache Dubbo 服务配备了HTTP 网关能力。通过 dubbo-proxy 插件,可以轻松地将 Dubbo Service 发布为 HTTP 服务。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/apisix-plugin/1.png" alt="架构图"></p> |
| <h2 id="如何使用">如何使用</h2> |
| <h3 id="入门篇安装使用">入门篇:安装使用</h3> |
| <blockquote> |
| <p>这里我们建议使用 Apache APISIX 2.11 版本镜像进行安装。该版本的 APISIX-Base 中已默认编译了 Dubbo 模块,可直接使用 <code>dubbo-proxy</code> 插件。</p> |
| </blockquote> |
| <p>在接下来的操作中,我们将使用 <a href="https://github.com/apache/dubbo-samples"><code>dubbo-samples</code></a> 项目进行部分展示。该项目是一些使用 Apache Dubbo 实现的 Demo 应用,本文中我们采用其中的一个子模块作为 Dubbo Provider。</p> |
| <p>在进入正式操作前,我们先简单看下 Dubbo 接口的定义、配置以及相关实现。</p> |
| <h4 id="接口实现一览">接口实现一览</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">DemoService</span> { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * standard samples dubbo infterace demo |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param context pass http infos |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @return Map&lt;String, Object&gt;&lt;/&gt; pass to response http |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> **/</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String, Object<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">apisixDubbo</span>(Map<span style="color:#719e07">&lt;</span>String, Object<span style="color:#719e07">&gt;</span> httpRequestContext); |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>如上所示,Dubbo 接口的定义是固定的。即方法参数中 <code>Map</code> 表示 APISIX 传递给 Dubbo Provider 关于 HTTP request 的一些信息(如:header、body&hellip;)。而方法返回值的 <code>Map</code> 表示 Dubbo Provider 传递给 APISIX 要如何返回 HTTP response 的一些信息。</p> |
| <p>接口信息配置好之后可通过 XML 配置方式发布 DemoService。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#586e75">&lt;!-- service implementation, as same as regular local bean --&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;bean</span> id=<span style="color:#2aa198">&#34;demoService&#34;</span> class=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.provider.DemoServiceImpl&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">&lt;!-- declare the service interface to be exported --&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:service</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.apisix.DemoService&#34;</span> ref=<span style="color:#2aa198">&#34;demoService&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><p>通过上述配置后,Consumer 可通过 <code>org.apache.dubbo.samples.apisix.DemoService</code> 访问其中的<code>apisixDubbo</code> 方法。具体接口实现如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">DemoServiceImpl</span> <span style="color:#268bd2">implements</span> DemoService { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Map<span style="color:#719e07">&lt;</span>String, Object<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">apisixDubbo</span>(Map<span style="color:#719e07">&lt;</span>String, Object<span style="color:#719e07">&gt;</span> httpRequestContext) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (Map.Entry<span style="color:#719e07">&lt;</span>String, Object<span style="color:#719e07">&gt;</span> entry : httpRequestContext.entrySet()) { |
| </span></span><span style="display:flex;"><span> System.out.println(<span style="color:#2aa198">&#34;Key = &#34;</span> <span style="color:#719e07">+</span> entry.getKey() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, Value = &#34;</span> <span style="color:#719e07">+</span> entry.getValue()); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String, Object<span style="color:#719e07">&gt;</span> ret <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> HashMap<span style="color:#719e07">&lt;</span>String, Object<span style="color:#719e07">&gt;</span>(); |
| </span></span><span style="display:flex;"><span> ret.put(<span style="color:#2aa198">&#34;body&#34;</span>, <span style="color:#2aa198">&#34;dubbo success\n&#34;</span>); <span style="color:#586e75">// http response body</span> |
| </span></span><span style="display:flex;"><span> ret.put(<span style="color:#2aa198">&#34;status&#34;</span>, <span style="color:#2aa198">&#34;200&#34;</span>); <span style="color:#586e75">// http response status</span> |
| </span></span><span style="display:flex;"><span> ret.put(<span style="color:#2aa198">&#34;test&#34;</span>, <span style="color:#2aa198">&#34;123&#34;</span>); <span style="color:#586e75">// http response header</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> ret; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>上述代码中,<code>DemoServiceImpl</code> 会打印接收到的 <code>httpRequestContext</code>,并通过返回包含有指定 Key 的 Map 对象去描述该 Dubbo 请求的 HTTP 响应。</p> |
| <h4 id="操作步骤">操作步骤</h4> |
| <ol> |
| <li>启动 <a href="https://github.com/apache/dubbo-samples/tree/master/2-advanced/dubbo-samples-tengine#install-dubbo"><code>dubbo-samples</code></a>。</li> |
| <li>在 <code>config.yaml</code> 文件中进行 <code>dubbo-proxy</code> 插件启用。</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#586e75"># Add this in config.yaml</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">plugins</span>: |
| </span></span><span style="display:flex;"><span> - ... <span style="color:#586e75"># plugin you need</span> |
| </span></span><span style="display:flex;"><span> - dubbo-proxy |
| </span></span></code></pre></div><ol start="3"> |
| <li>创建指向 Dubbo Provider 的 Upstream。</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>curl http://127.0.0.1:9180/apisix/admin/upstreams/1 -H <span style="color:#2aa198">&#39;X-API-KEY: edd1c9f034335f136f87ad84b625c8f1&#39;</span> -X PUT -d <span style="color:#2aa198">&#39; |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198">{ |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;nodes&#34;: { |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;127.0.0.1:20880&#34;: 1 |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> }, |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;type&#34;: &#34;roundrobin&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198">}&#39;</span> |
| </span></span></code></pre></div><ol start="4"> |
| <li>为 DemoService 暴露一个 HTTP 路由。</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>curl http://127.0.0.1:9180/apisix/admin/routes/1 -H <span style="color:#2aa198">&#39;X-API-KEY: edd1c9f034335f136f87ad84b625c8f1&#39;</span> -X PUT -d <span style="color:#2aa198">&#39; |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198">{ |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;host&#34;: &#34;example.org&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;uris&#34;: [ |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;/demo&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> ], |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;plugins&#34;: { |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;dubbo-proxy&#34;: { |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;service_name&#34;: &#34;org.apache.dubbo.samples.apisix.DemoService&#34;, |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;service_version&#34;: &#34;0.0.0&#34;, |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;method&#34;: &#34;apisixDubbo&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> } |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> }, |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;upstream_id&#34;: 1 |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198">}&#39;</span> |
| </span></span></code></pre></div><ol start="5"> |
| <li>使用 curl 命令请求 Apache APISIX,并查看返回结果。</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>curl http://127.0.0.1:9080/demo -H <span style="color:#2aa198">&#34;Host: example.org&#34;</span> -X POST --data <span style="color:#2aa198">&#39;{&#34;name&#34;: &#34;hello&#34;}&#39;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>&lt; HTTP/1.1 <span style="color:#2aa198">200</span> OK |
| </span></span><span style="display:flex;"><span>&lt; Date: Sun, <span style="color:#2aa198">26</span> Dec <span style="color:#2aa198">2021</span> 11:33:27 GMT |
| </span></span><span style="display:flex;"><span>&lt; Content-Type: text/plain; <span style="color:#268bd2">charset</span><span style="color:#719e07">=</span>utf-8 |
| </span></span><span style="display:flex;"><span>&lt; Content-Length: <span style="color:#2aa198">14</span> |
| </span></span><span style="display:flex;"><span>&lt; Connection: keep-alive |
| </span></span><span style="display:flex;"><span>&lt; test: <span style="color:#2aa198">123</span> |
| </span></span><span style="display:flex;"><span>&lt; Server: APISIX/2.11.0 |
| </span></span><span style="display:flex;"><span>&lt; |
| </span></span><span style="display:flex;"><span>dubbo success |
| </span></span></code></pre></div><p>:::note 说明 |
| 上述代码返回中包含了 <code>test: 123</code> Header,以及 <code>dubbo success</code> 字符串作为 Body 体。这与我们在 <code>DemoServiceImpl</code> 编码的预期效果一致。 |
| :::</p> |
| <ol start="6"> |
| <li>查看 Dubbo Provider 的日志。</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>Key = content-length, Value = 17 |
| </span></span><span style="display:flex;"><span>Key = host, Value = example.org |
| </span></span><span style="display:flex;"><span>Key = content-type, Value = application/x-www-form-urlencoded |
| </span></span><span style="display:flex;"><span>Key = body, Value = [B@70754265 |
| </span></span><span style="display:flex;"><span>Key = accept, Value = */* |
| </span></span><span style="display:flex;"><span>Key = user-agent, Value = curl/7.80.0 |
| </span></span></code></pre></div><p>:::note 说明 |
| 通过 <code>httpRequestContext</code> 可以拿到 HTTP 请求的 Header 和 Body。其中 Header 会作为 Map 元素,而 Body 中 Key 值是固定的字符串&quot;body&quot;,Value 则代表 Byte 数组。 |
| :::</p> |
| <h3 id="进阶篇复杂场景示例">进阶篇:复杂场景示例</h3> |
| <p>在上述的简单用例中可以看出,我们确实通过 Apache APISIX 将 Dubbo Service 发布为一个 HTTP 服务,但是在使用过程中的限制也非常明显。比如:接口的参数和返回值都必须要是 <code>Map&lt;String, Object&gt;</code>。</p> |
| <p>那么,如果项目中出现已经定义好、但又不符合上述限制的接口,该如何通过 Apache APISIX 来暴露 HTTP 服务呢?</p> |
| <h4 id="操作步骤-1">操作步骤</h4> |
| <p>针对上述场景,我们可以通过 HTTP Request Body 描述要调用的 Service 和 Method 以及对应参数,再利用 Java 的反射机制实现目标方法的调用。最后将返回值序列化为 JSON,并写入到 HTTP Response Body 中。</p> |
| <p>这样就可以将 Apache APISIX 的 「HTTP to Dubbo」 能力进一步加强,并应用到所有已存在的 Dubbo Service 中。具体操作可参考下方:</p> |
| <ol> |
| <li>为已有项目增加一个 Dubbo Service 用来统一处理 HTTP to Dubbo 的转化。</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">DubboInvocationParameter</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> String type; |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> String value; |
| </span></span><span style="display:flex;"><span>} |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">DubboInvocation</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> String service; |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> String method; |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> DubboInvocationParameter<span style="color:#719e07">[]</span> parameters; |
| </span></span><span style="display:flex;"><span>} |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">HTTP2DubboService</span> { |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String, Object<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">invoke</span>(Map<span style="color:#719e07">&lt;</span>String, Object<span style="color:#719e07">&gt;</span> context) <span style="color:#268bd2">throws</span> Exception; |
| </span></span><span style="display:flex;"><span>} |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">@Component</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">HTTP2DubboServiceImpl</span> <span style="color:#268bd2">implements</span> HTTP2DubboService { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Autowired</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> ApplicationContext appContext; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Map<span style="color:#719e07">&lt;</span>String, Object<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">invoke</span>(Map<span style="color:#719e07">&lt;</span>String, Object<span style="color:#719e07">&gt;</span> context) <span style="color:#268bd2">throws</span> Exception { |
| </span></span><span style="display:flex;"><span> DubboInvocation invocation <span style="color:#719e07">=</span> JSONObject.parseObject((<span style="color:#dc322f">byte</span><span style="color:#719e07">[]</span>) context.get(<span style="color:#2aa198">&#34;body&#34;</span>), DubboInvocation.class); |
| </span></span><span style="display:flex;"><span> Object<span style="color:#719e07">[]</span> args <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> Object<span style="color:#719e07">[</span>invocation.getParameters().size()<span style="color:#719e07">]</span>; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (<span style="color:#dc322f">int</span> i <span style="color:#719e07">=</span> 0; i <span style="color:#719e07">&lt;</span> args.length; i<span style="color:#719e07">++</span>) { |
| </span></span><span style="display:flex;"><span> DubboInvocationParameter parameter <span style="color:#719e07">=</span> invocation.getParameters().get(i); |
| </span></span><span style="display:flex;"><span> args<span style="color:#719e07">[</span>i<span style="color:#719e07">]</span> <span style="color:#719e07">=</span> JSONObject.parseObject(parameter.getValue(), Class.forName(parameter.getType())); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> Object svc <span style="color:#719e07">=</span> appContext.getBean(Class.forName(invocation.getService())); |
| </span></span><span style="display:flex;"><span> Object result <span style="color:#719e07">=</span> svc.getClass().getMethod(invocation.getMethod()).invoke(args); |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String, Object<span style="color:#719e07">&gt;</span> httpResponse <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> HashMap<span style="color:#719e07">&lt;&gt;</span>(); |
| </span></span><span style="display:flex;"><span> httpResponse.put(<span style="color:#2aa198">&#34;status&#34;</span>, 200); |
| </span></span><span style="display:flex;"><span> httpResponse.put(<span style="color:#2aa198">&#34;body&#34;</span>, JSONObject.toJSONString(result)); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> httpResponse; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><ol start="2"> |
| <li>通过如下命令请求来发起相关调用。</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>curl http://127.0.0.1:9080/demo -H <span style="color:#2aa198">&#34;Host: example.org&#34;</span> -X POST --data <span style="color:#2aa198">&#39; |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198">{ |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;service&#34;: &#34;org.apache.dubbo.samples.apisix.DemoService&#34;, |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;method&#34;: &#34;createUser&#34;, |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;parameters&#34;: [ |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> { |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;type&#34;: &#34;org.apache.dubbo.samples.apisix.User&#34;, |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;value&#34;: &#34;{&#39;</span>name<span style="color:#2aa198">&#39;: &#39;</span>hello<span style="color:#2aa198">&#39;}&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> } |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> ] |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198">}&#39;</span> |
| </span></span></code></pre></div><h2 id="总结">总结</h2> |
| <p>本文为大家介绍了如何借助 Apache APISIX 实现 Dubbo Service 的代理,通过引入 <code>dubbo-proxy</code> 插件便可为 Dubbo 框架的后端系统构建更简单更高效的流量链路。</p> |
| <p>希望通过上述操作步骤和用例场景分享,能为大家在相关场景的使用提供借鉴思路。更多关于 <code>dubbo-proxy</code> 插件的介绍与使用可参考<a href="https://apisix.apache.org/docs/apisix/plugins/dubbo-proxy/">官方文档</a>。</p></description></item><item><title>Blog: Dubbo 跨语言调用神兽:dubbo-go-pixiu</title><link>https://dubbo.apache.org/zh-cn/blog/2021/08/25/dubbo-%E8%B7%A8%E8%AF%AD%E8%A8%80%E8%B0%83%E7%94%A8%E7%A5%9E%E5%85%BDdubbo-go-pixiu/</link><pubDate>Wed, 25 Aug 2021 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2021/08/25/dubbo-%E8%B7%A8%E8%AF%AD%E8%A8%80%E8%B0%83%E7%94%A8%E7%A5%9E%E5%85%BDdubbo-go-pixiu/</guid><description> |
| <h2 id="pixiu-是什么">Pixiu 是什么</h2> |
| <p>在回答 Pixiu 是什么之前,我们简单解释一下 Dubbo 是什么。Dubbo 是一个开源的高性能 RPC 框架,有着丰富的服务治理能力以及优秀的扩展能力。Dubbo 更扩展出 Dubbo-go【1】,为用户提供了 Golang 的 Dubbo 解决方案,打通了两种语言之间的隔阂,使 Dubbo 更加贴近云原生。</p> |
| <p>Dubbo-go 作为 Golang 服务,实现与 Dubbo 服务之间的相互调用。然而,在日常使用场景中,用户往往有把 Dubbo 服务以 RESTful 风格向外暴露的需求同时也要兼顾内部 Dubbo 调用。为了解决这种场景,作为 Dubbo API 网关的 Pixiu【2】 (中文: 貔貅, 曾用名 dubbo-go-proxy) 便应运而生。之所以采用 Pixiu 这个名称,是因为 Java 同类产品 Zuul 的意象是一个西方怪兽,Pixiu 作为一个国产产品,就用了我们中国的一个类似的神兽貔貅作为项目名称。也同时表达了 Dubbo 社区希望扩展出一整套云原生生态链的决心。</p> |
| <p>目前 Dubbo 多语言生态,发展最好的自然是 Java,其次是 Golang,其他语言都差强人意。dubbo-go-pixiu 项目是一个基于 dubbo-go 发展起来的项目,目前接口协议层支持的是七层的 HTTP 请求调用,计划在未来的 0.5 版本中支持 gRPC 请求调用,其另外一个使命是作为一种新的 dubbo 多语言解决方案。</p> |
| <h2 id="为什么使用-pixiu">为什么使用 Pixiu</h2> |
| <p>Pixiu 是基于 Dubbogo 的云原生、高性能、可扩展的微服务 API 网关。作为一款网关产品,Pixiu 帮助用户轻松创建、发布、维护、监控和保护任意规模的 API ,接受和处理成千上万个并发 API 调用,包括流量管理、 CORS 支持、授权和访问控制、限制、监控,以及 API 版本管理。除此以外,作为 Dubbo 的衍生产品,Pixiu 可以帮助 Dubbo 用户进行协议转换,实现跨系统、跨协议的服务能力互通。</p> |
| <p>Pixiu 的整体设计遵守以下原则:</p> |
| <ul> |
| <li>High performance: 高吞吐量以及毫秒级的延时。</li> |
| <li>可扩展: 通过 go-plugin,用户可以根据自己的需求延展 Pixiu 的功能。</li> |
| <li>简单可用: 用户通过少量配置,即可上线。</li> |
| </ul> |
| <h2 id="pixiu-的特性及核心功能">Pixiu 的特性及核心功能</h2> |
| <ul> |
| <li>为 RESTful API 和 Dubbo API 提供支持</li> |
| </ul> |
| <p>非 RESTful 风格的 API 和 Dubbo 协议的服务往往需要修改才可以以 RESTful API 风格对外开放。Pixiu 提供协议转换功能,通过 Pixiu,开发者可以将自己的 HTTP API 或 Dubbo API 通过配置,以 RESTful API 风格对外开放。v0.2.1 版本已支持基于泛化调用的 HTTP 至 Dubbo 的协议转换以及 HTTP 协议的转发。在后续的版本,社区将会增加对 gRPC 和 http2 协议的支持。</p> |
| <ul> |
| <li>面向用户的配置方式</li> |
| </ul> |
| <p>一般的网关的配置往往繁琐且复杂。Pixiu,目标作为一款易用的网关产品,在设计上拥有三层配置层级,Gateway 层全局配置, API resource 层配置以及 HTTP verbs 方法层配置。通过三个不同层级的配置,既可以实现深度的定制,亦支持统一的默认配置;同时,支持本地的配置文件,亦可使用统一配置服务器。另外,还提供控制台模块,通过控制台模块,支持配置的热更新。Pixiu 配套配套的控制台界面也在同步开发中。</p> |
| <ul> |
| <li>通用功能的集成</li> |
| </ul> |
| <p>重试、熔断、流量控制、访问控制等通用功能不再需要在每个后端服务上重复实现。使用 Pixiu,通过配置 filter ,开发者可以进行全局的控制,亦可以根据 API 配置各自的规则。因此开发者可以专注于业务逻辑和服务,而不是将时间用在维护基础设施上。</p> |
| <ul> |
| <li>可扩展</li> |
| </ul> |
| <p>不同的使用场景有着各自独特的需求。为满足不同用户的定制化需求,Pixiu 使用了插件模式。开发者可以通过编写 go plugin,将自身特有的业务逻辑以 filter 形式内嵌至 Pixiu 网关中,实现诸如企业登录鉴权等功能。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/1/01/01/dubbo-go-pixiu/fd38da297d095e4c3af1c89b18804ef1.webp" alt="img"></p> |
| <p>图 1: Pixiu 核心功能列表</p> |
| <h2 id="pixiu-的架构设计">Pixiu 的架构设计</h2> |
| <p><img src="https://dubbo.apache.org/imgs/blog/1/01/01/dubbo-go-pixiu/2b2fd6ea1cc0375392919d9e0c181f2b.webp" alt="img"></p> |
| <p>图 2: Pixiu 架构</p> |
| <p>貔貅: 即 dubbo-go-pixiu,由四个主要模块:Listener、Router、Filters 和 Clients 组成;</p> |
| <ul> |
| <li>Dubbo Cluster: Dubbo 服务所在集群,包含一个或多个 Dubbo Services;</li> |
| <li>Other Cluster: Dubbo 以外的服务所在集群,现支持 HTTP 服务,未来将拓展支持 gRPC 等其他服务;</li> |
| <li>Registry Center: 注册中心,维护每个业务服务的调用地址信息;</li> |
| <li>Metadata Center: 元数据中心,维护每个业务服务的配置信息以及存储 Pixiu 本身的配置信息。</li> |
| </ul> |
| <p>作为 Dubbo 所衍生的 API 网关,Pixiu 使用 Golang 搭建,主要因为: 1. Golang 的 G-M-P,net poller 等特性使 Golang 非常适合构建 IO 密集型应用;2. 使用 Golang 可以直接引入 Dubbo-go 中的一些组建,简化开发。</p> |
| <p>整个 Pixiu 大致可以拆分为四个主要模块:Listener、Router、Filters 和 Client。</p> |
| <h3 id="1listener">1、Listener</h3> |
| <p>在 Pixiu 中,Listener 代表外部可以访问 Pixiu 的方式。通过配置指定协议类型,地址,端口等属性,暴露 Gateway。现阶段暂支持 HTTP 协议,未来将会加入 gRPC。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>listeners: |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> - name: &#34;net/http&#34; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> address: |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> socket_address: |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> protocol_type: &#34;HTTP&#34; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> address: &#34;0.0.0.0&#34; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> port: 8888 |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> config: |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> idle_timeout: 5s |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> read_timeout: 5s |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> write_timeout: 5s |
| </span></span></code></pre></div><h3 id="2router">2、Router</h3> |
| <p>Router 是 Pixiu 的路由组件。根据配置文件,Pixiu 将对外暴露的 URLs 以树的形势存储于内存中,当请求到了 router 组件时,即会根据 URL 及 HTTP 方法查找到对应的后端服务及其 API 配置,并将信息封装于请求中,为后续 filter,及 client 的调用提供足够的内容。</p> |
| <p>现阶段,Router 提供以下功能:</p> |
| <ul> |
| <li>支持请求一对一转发路由配置或 wildcard 路由配置。</li> |
| <li>支持 HTTP 请求的转发到后端 HTTP 服务。</li> |
| <li>支持 HTTP 请求转化为 dubbo 泛化调用请求。</li> |
| </ul> |
| <h3 id="3filters">3、Filters</h3> |
| <p>Filter 是 Pixiu 实现额外功能及其扩展性的主要组件。其实现类似于 Dubbo-go 中的 filter,根据配置中 filter 的指定,生成调用链,从而在调用后端服务前,将各 filter 中的逻辑运行一遍,实现节流,日志等功能。</p> |
| <p>用户如果需要客制化的 filter,可通过编写 go-plugin 实现。在配置中,可通过类似如下配置,加载 .so 文件,并在 API config 中指定使用的 plugin group,plugin name 实现。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>pluginFilePath: &#34;&#34; |
| </span></span><span style="display:flex;"><span>pluginsGroup: |
| </span></span><span style="display:flex;"><span> - groupName: &#34;group1&#34; |
| </span></span><span style="display:flex;"><span> plugins: |
| </span></span><span style="display:flex;"><span> - name: &#34;rate limit&#34; |
| </span></span><span style="display:flex;"><span> version: &#34;0.0.1&#34; |
| </span></span><span style="display:flex;"><span> priority: 1000 |
| </span></span><span style="display:flex;"><span> externalLookupName: &#34;ExternalPluginRateLimit&#34; |
| </span></span><span style="display:flex;"><span> - name: &#34;access&#34; |
| </span></span><span style="display:flex;"><span> version: &#34;0.0.1&#34; |
| </span></span><span style="display:flex;"><span> priority: 1000 |
| </span></span><span style="display:flex;"><span> externalLookupName: &#34;ExternalPluginAccess&#34; |
| </span></span><span style="display:flex;"><span> - groupName: &#34;group2&#34; |
| </span></span><span style="display:flex;"><span> plugins: |
| </span></span><span style="display:flex;"><span> - name: &#34;blacklist&#34; |
| </span></span><span style="display:flex;"><span> version: &#34;0.0.1&#34; |
| </span></span><span style="display:flex;"><span> priority: 1000 |
| </span></span><span style="display:flex;"><span> externalLookupName: &#34;ExternalPluginBlackList&#34; |
| </span></span></code></pre></div><h3 id="4client">4、Client</h3> |
| <p>Client 负责调用具体服务。现阶段,Pixiu 支持 HTTP 与 Dubbo 的后端服务。社区将逐渐增加 gRPC 等其他 Client 以满足不同的协议。</p> |
| <p>HTTP client 的实现相对简单,根据 Router 中获取的后端服务信息,通过 Golang 官方包 net/http 生成请求并调用。</p> |
| <p>Dubbo client 的实现对比 HTTP client 会稍微复杂,其基础为 Dubbo 服务的泛化调用。泛化调用技术是 Dubbo 提供的一个很基础的功能只需要知道调用的方法名、参数类型和返回值类型,即可发起服务调用。客户端对服务端的泛化调用既可以通过注册中心发现服务,也可以直连服务端,实现对服务的动态调用。</p> |
| <p>如下面代码所示,Pixiu 通过动态配置 referenceConfig,然后通过 GetRPCService 生成 Dubbo 的 Generic Client(泛化调用客户端)进行下一步的调用。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-gdscript3" data-lang="gdscript3"><span style="display:flex;"><span>referenceConfig :<span style="color:#719e07">=</span> dg<span style="color:#719e07">.</span>NewReferenceConfig(irequest<span style="color:#719e07">.</span>Interface, context<span style="color:#719e07">.</span>TODO()) |
| </span></span><span style="display:flex;"><span> referenceConfig<span style="color:#719e07">.</span>InterfaceName <span style="color:#719e07">=</span> irequest<span style="color:#719e07">.</span>Interface |
| </span></span><span style="display:flex;"><span> referenceConfig<span style="color:#719e07">.</span>Cluster <span style="color:#719e07">=</span> constant<span style="color:#719e07">.</span>DEFAULT_CLUSTER |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">var</span> registers []string |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> k :<span style="color:#719e07">=</span> <span style="color:#b58900">range</span> dgCfg<span style="color:#719e07">.</span>Registries { |
| </span></span><span style="display:flex;"><span> registers <span style="color:#719e07">=</span> append(registers, k) |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> referenceConfig<span style="color:#719e07">.</span>Registry <span style="color:#719e07">=</span> strings<span style="color:#719e07">.</span>Join(registers, <span style="color:#2aa198">&#34;,&#34;</span>) |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> len(irequest<span style="color:#719e07">.</span>DubboBackendConfig<span style="color:#719e07">.</span>Protocol) <span style="color:#719e07">==</span> <span style="color:#2aa198">0</span> { |
| </span></span><span style="display:flex;"><span> referenceConfig<span style="color:#719e07">.</span>Protocol <span style="color:#719e07">=</span> dubbo<span style="color:#719e07">.</span>DUBBO |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> referenceConfig<span style="color:#719e07">.</span>Protocol <span style="color:#719e07">=</span> irequest<span style="color:#719e07">.</span>DubboBackendConfig<span style="color:#719e07">.</span>Protocol |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> referenceConfig<span style="color:#719e07">.</span>Version <span style="color:#719e07">=</span> irequest<span style="color:#719e07">.</span>DubboBackendConfig<span style="color:#719e07">.</span>Version |
| </span></span><span style="display:flex;"><span> referenceConfig<span style="color:#719e07">.</span>Group <span style="color:#719e07">=</span> irequest<span style="color:#719e07">.</span>Group |
| </span></span><span style="display:flex;"><span> referenceConfig<span style="color:#719e07">.</span>Generic <span style="color:#719e07">=</span> <span style="color:#268bd2">true</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> len(irequest<span style="color:#719e07">.</span>DubboBackendConfig<span style="color:#719e07">.</span>Retries) <span style="color:#719e07">==</span> <span style="color:#2aa198">0</span> { |
| </span></span><span style="display:flex;"><span> referenceConfig<span style="color:#719e07">.</span>Retries <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;3&#34;</span> |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> referenceConfig<span style="color:#719e07">.</span>Retries <span style="color:#719e07">=</span> irequest<span style="color:#719e07">.</span>DubboBackendConfig<span style="color:#719e07">.</span>Retries |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> dc<span style="color:#719e07">.</span>lock<span style="color:#719e07">.</span>Lock() |
| </span></span><span style="display:flex;"><span> defer dc<span style="color:#719e07">.</span>lock<span style="color:#719e07">.</span>Unlock() |
| </span></span><span style="display:flex;"><span> referenceConfig<span style="color:#719e07">.</span>GenericLoad(key) |
| </span></span><span style="display:flex;"><span> clientService :<span style="color:#719e07">=</span> referenceConfig<span style="color:#719e07">.</span>GetRPCService()<span style="color:#719e07">.</span>(<span style="color:#719e07">*</span>dg<span style="color:#719e07">.</span>GenericService) |
| </span></span></code></pre></div><p>实际上,在泛化调用的客户端中,实际执行泛化调用的关键步骤是 Dubbo-go 中的 generic_filter (如下代码片段)。在调用 generic_filter 的 Invoke 时,约定 invocation 参数列表第一个为方法名,第二个为参数类型列表,第三个为参数值列表。generic_filter 将用户请求的参数值列表转化为统一格式的 map(代码中的 struct2MapAll ),将类( golang 中为 struct )的正反序列化操作变成 map 的正反序列化操作。这使得无需 POJO 描述通过硬编码注入 hessain 库,从而完成 Dubbo 服务的泛化调用。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-gdscript3" data-lang="gdscript3"><span style="display:flex;"><span><span style="color:#719e07">func</span> (ef <span style="color:#719e07">*</span>GenericFilter) Invoke(ctx context<span style="color:#719e07">.</span>Context, invoker protocol<span style="color:#719e07">.</span>Invoker, invocation protocol<span style="color:#719e07">.</span>Invocation) protocol<span style="color:#719e07">.</span>Result { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> invocation<span style="color:#719e07">.</span>MethodName() <span style="color:#719e07">==</span> constant<span style="color:#719e07">.</span>GENERIC <span style="color:#719e07">&amp;&amp;</span> len(invocation<span style="color:#719e07">.</span>Arguments()) <span style="color:#719e07">==</span> <span style="color:#2aa198">3</span> { |
| </span></span><span style="display:flex;"><span> oldArguments :<span style="color:#719e07">=</span> invocation<span style="color:#719e07">.</span>Arguments() |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> oldParams, ok :<span style="color:#719e07">=</span> oldArguments[<span style="color:#2aa198">2</span>]<span style="color:#719e07">.</span>([]interface{}); ok { |
| </span></span><span style="display:flex;"><span> newParams :<span style="color:#719e07">=</span> make([]hessian<span style="color:#719e07">.</span>Object, <span style="color:#2aa198">0</span>, len(oldParams)) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> i :<span style="color:#719e07">=</span> <span style="color:#b58900">range</span> oldParams { |
| </span></span><span style="display:flex;"><span> newParams <span style="color:#719e07">=</span> append(newParams, hessian<span style="color:#719e07">.</span>Object(struct2MapAll(oldParams[i]))) |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> newArguments :<span style="color:#719e07">=</span> []interface{}{ |
| </span></span><span style="display:flex;"><span> oldArguments[<span style="color:#2aa198">0</span>], |
| </span></span><span style="display:flex;"><span> oldArguments[<span style="color:#2aa198">1</span>], |
| </span></span><span style="display:flex;"><span> newParams, |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> newInvocation :<span style="color:#719e07">=</span> invocation2<span style="color:#719e07">.</span>NewRPCInvocation(invocation<span style="color:#719e07">.</span>MethodName(), newArguments, invocation<span style="color:#719e07">.</span>Attachments()) |
| </span></span><span style="display:flex;"><span> newInvocation<span style="color:#719e07">.</span>SetReply(invocation<span style="color:#719e07">.</span>Reply()) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> invoker<span style="color:#719e07">.</span>Invoke(ctx, newInvocation) |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> invoker<span style="color:#719e07">.</span>Invoke(ctx, invocation) |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><h2 id="总结">总结</h2> |
| <p>通过上面的四个模块以及注册中心的简单介绍不难发现,当请求通过 listener 被 Pixiu 接收后,请求被传入 router 中。router 根据接口的配置,从原请求中找到目标后端服务连同相关 API 配置下发到 filter 组件。filter 组件根据原请求、 API 配置等信息顺序执行,最终请求到达 client, 通过 client 调用后端服务。</p> |
| <h3 id="pixiu-的未来">Pixiu 的未来</h3> |
| <p><img src="https://dubbo.apache.org/imgs/blog/1/01/01/dubbo-go-pixiu/e57050f224f658b96cd6bd917050b259.webp" alt="img"> |
| 图 3: Pixiu 迭代里程碑</p> |
| <p>Pixiu 作为网关产品外,其衍生项目也会在我们的未来计划中,主要目的是提供更好的可用性。例如,由于 Golang 语言缺乏原生的注解, 因此 Dubbo-go 需要通过配置文件方式生成服务的元数据写入注册中心。开课啦教育公司相关同学写了一个扫描代码的工具 <a href="https://github.com/jack15083/dubbo-go-proxy-tool">https://github.com/jack15083/dubbo-go-proxy-tool</a>,在每个 RPC 服务方法前加上对应的注释,从而在服务启动前通过扫描注释生成元数据。Pixiu 也计划在未来的版本上通过提供 package,允许服务通过注释借助 <a href="https://github.com/MarcGrol/golangAnnotations">https://github.com/MarcGrol/golangAnnotations</a> 生成 API 配置并注册到 Pixiu 上。</p> |
| <p>Pixiu 目前的定位是一个七层协议网关,其最初版本是被定义成一个 Dubbo 的服务网关。作为云时代的产品,Pixiu 的发展方向必然是面向云原生的。现在的版本为 0.2.1, 已经实现基本的 Dubbo/Http 服务代理和部分的网关通用功能。目前正在开发中的 0.4 及其后续版本支持 gRPC 和 Spring Cloud 服务调用, 后续还将提供 MQ 服务支持。另外,社区将继续优化配置方式,降低用户的使用难度,继续优化官方的 filter,使 Pixiu 可以在官方层面实现更多的网关通用功能。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/1/01/01/dubbo-go-pixiu/0c1afe00699eb3e5cc022e48966ef5a6.webp" alt="img"></p> |
| <p>在未来的一年内,社区计划支持 xDS API,将 Pixiu 演化为 Dubbo mesh 的 sidecar。其最终目的就是:在现有的 dubbo mesh 形态中演化出 Proxy Service Mesh 形态。基于这个形态,Js、Python、PHP、Ruby 和 Perl 等脚本语言程序除了收获 dubbo mesh 原有的技术红利之外,大概率还能收获性能上的提升。</p> |
| <p>Pixiu 在 Dubbo Mesh 中的终极目的是:把东西向和南北向数据面流量逐步统一 Pixiu 中的同时,让它逐步具备 Application Runtime 的能力,作为 Dubbo 多语言生态的关键解决方案。</p> |
| <p>相关链接:</p> |
| <p>【1】Dubbo-go:https://github.com/apache/dubbo-go</p> |
| <p>【2】Pixiu:https://github.com/apache/dubbo-go-pixiu</p> |
| <p>冯振宇,Apache Dubbo Committer,目前负责管理香港一家消费品公司的 IT 部门整个团队。2020 年夏天 偶然看到了介绍 dubbogo 的文章后加入了 dubbogo 社区,目前在主导 Pixiu 0.4.0 版本的开发。</p></description></item></channel></rss> |