<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>自定义扩展 on Apache Dubbo</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/overview/mannual/java-sdk/tasks/extensibility/</link><description>Recent content in 自定义扩展 on Apache Dubbo</description><generator>Hugo</generator><language>zh-cn</language><atom:link href="https://deploy-preview-3202--dubbo.netlify.app/zh-cn/overview/mannual/java-sdk/tasks/extensibility/index.xml" rel="self" type="application/rss+xml"/><item><title>自定义 SPI 扩展的基本步骤</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/overview/mannual/java-sdk/tasks/extensibility/spi/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/overview/mannual/java-sdk/tasks/extensibility/spi/</guid><description>&lt;p>下面以 &lt;code>RPC 协议插件&lt;/code> 为例，说明如何利用 Dubbo 提供的 SPI 插件提供一个自定义的 RPC 协议实现。如果想了解 SPI 机制的工作原理以及框架内置的 SPI 扩展点列表，请查看 &lt;a href="https://deploy-preview-3202--dubbo.netlify.app/zh-cn/overview/mannual/java-sdk/reference-manual/spi/overview">参考手册 - SPI扩展&lt;/a>。&lt;/p>
&lt;h2 id="1-提供-spi-插件实现类">1. 提供 SPI 插件实现类&lt;/h2>
&lt;p>提供一个 Java 类实现 &lt;code>org.apache.dubbo.rpc.Protocol&lt;/code> 接口。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> com.spi.demo;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.Protocol;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Activate&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">CustomizedProtocol&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> Protocol {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#586e75">// ...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="2-在指定文件配置实现类">2. 在指定文件配置实现类&lt;/h2>
&lt;p>在应用 &lt;code>resources/META-INF/services/&lt;/code> 目录下添加 &lt;code>org.apache.dubbo.rpc.Protocol&lt;/code> 文件，文件中增加如下配置：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-properties" data-lang="properties">&lt;span style="display:flex;">&lt;span>customized&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">com.spi.demo.CustomizedProtocol&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>

&lt;div class="alert alert-info" role="alert">
&lt;h4 class="alert-heading">配置注意事项&lt;/h4>

 &lt;ul>
&lt;li>文件名必须为 SPI 插件定义的 package 全路径名，具体取决于你要扩展的 SPI 定义，如示例中的 &lt;code>resources/META-INF/services/org.apache.dubbo.rpc.Protocol&lt;/code>。&lt;/li>
&lt;li>文件中的内容必须是 &lt;code>key=value&lt;/code> 形式，其中 &lt;code>key&lt;/code> 可随便定义，但建议增加特定前缀以避免与 Dubbo 内置实现重名，&lt;code>value&lt;/code> 必须设置为扩展类实现的全路径名。&lt;/li>
&lt;/ul>


&lt;/div>

&lt;h2 id="3-通过配置启用自定义协议实现">3. 通过配置启用自定义协议实现&lt;/h2>
&lt;p>在应用中修改协议配置，告诉 Dubbo 框架使用自定义协议：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># 使用 Spring Boot，可修改 application.yml 或 application.properties&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> protocol
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: customized
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>或者&lt;/p></description></item><item><title>Filter</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/overview/mannual/java-sdk/tasks/extensibility/filter/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/overview/mannual/java-sdk/tasks/extensibility/filter/</guid><description>&lt;p>在 &lt;a href="../../framework/filter/">RPC框架 - Filter请求拦截&lt;/a> 一节中，我们了解了 Filter 的工作机制，以及 Dubbo 框架提供的一些内置 Filter 实现。在本文中，我们来了解如何扩展自定义的过滤器实现：一个可以对返回的结果进行统一的处理、验证等统一 Filter 处理器，减少对开发人员的打扰。&lt;/p>
&lt;p>本示例的完整源码请参见 &lt;a href="https://github.com/apache/dubbo-samples/blob/master/10-task/dubbo-samples-extensibility/">dubbo-samples-extensibility&lt;/a>。除了本示例之外，Dubbo 核心仓库 apache/dubbo 以及扩展库 &lt;a href="https://github.com/apache/dubbo-spi-extensions/tree/master/dubbo-filter-extensions/">apache/dubbo-spi-extensions&lt;/a> 中的众多 Filter 实现，都可以作为扩展参考实现。&lt;/p>
&lt;h2 id="任务详情">任务详情&lt;/h2>
&lt;p>对所有调用Provider服务的请求在返回的结果的后面统一添加&lt;code>'s customized AppendedFilter&lt;/code>。&lt;/p>
&lt;h2 id="实现方式">实现方式&lt;/h2>
&lt;p>在Provider中自定义一个Filter，在Filter中修改返回结果。&lt;/p>
&lt;h4 id="代码结构">代码结构&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-properties" data-lang="properties">&lt;span style="display:flex;">&lt;span>src
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-main
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-java
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-org
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-apache
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-dubbo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-samples
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-extensibility
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-filter
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-provider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-AppendedFilter.java &lt;span style="color:#2aa198">(实现Filter接口)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-resources
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-META-INF
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-application.properties &lt;span style="color:#2aa198">(Dubbo Provider配置文件)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-dubbo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-org.apache.dubbo.rpc.Filter &lt;span style="color:#2aa198">(纯文本文件)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="代码详情">代码详情&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> org.apache.dubbo.samples.extensibility.filter.provider;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.Filter;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.Result;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.Invoker;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.Invocation;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.RpcException;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.AsyncRpcResult;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">AppendedFilter&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> Filter {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> Result &lt;span style="color:#268bd2">invoke&lt;/span>(Invoker&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> invoker, Invocation invocation) &lt;span style="color:#268bd2">throws&lt;/span> RpcException {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Result result&lt;span style="color:#719e07">=&lt;/span> invoker.invoke(invocation);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Obtain the returned value&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Result appResponse &lt;span style="color:#719e07">=&lt;/span> ((AsyncRpcResult) result).getAppResponse();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Appended value&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> appResponse.setValue(appResponse.getValue()&lt;span style="color:#719e07">+&lt;/span>&lt;span style="color:#2aa198">&amp;#34;&amp;#39;s customized AppendedFilter&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> result;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="spi配置">SPI配置&lt;/h4>
&lt;p>在&lt;code>resources/META-INF/dubbo/org.apache.dubbo.rpc.Filter&lt;/code>文件中添加如下配置：&lt;/p></description></item><item><title>Protocol</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/overview/mannual/java-sdk/tasks/extensibility/protocol/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/overview/mannual/java-sdk/tasks/extensibility/protocol/</guid><description>&lt;p>在 &lt;a href="https://deploy-preview-3202--dubbo.netlify.app/zh-cn/overview/mannual/java-sdk/tasks/protocols/">通信协议&lt;/a> 一章中，我们了解了 Dubbo 内置的几个核心 RPC 协议 &lt;code>dubbo&lt;/code>、&lt;code>rest&lt;/code>、和&lt;code>tri&lt;/code> 以及它们的使用方式。本文讲解如何通过扩展 &lt;code>org.apache.dubbo.rpc.Protocol&lt;/code> SPI，提供自定义的 RPC 协议实现。&lt;/p>
&lt;p>自定义一套私有协议有两种方式，第一种是对原有的协议进行包装，添加一些特定的业务逻辑。另外一种是完全自定义一套协议。前者实现简单，在&lt;code>dubbo&lt;/code>中也是有广泛的使用，比如：&lt;code>ProtocolFilterWrapper&lt;/code>, &lt;code>QosProtocolWrapper&lt;/code>, &lt;code>ProtocolListenerWrapper&lt;/code>等。后者实现相对复杂，但却具有最大的灵活性，比如 Dubbo 框架内置的协议 &lt;code>dubbo&lt;/code>、&lt;code>triple&lt;/code> 协议都可以算作这种实现方式。&lt;/p>
&lt;p>本示例的完整源码请参见 &lt;a href="https://github.com/apache/dubbo-samples/tree/master/10-task/dubbo-samples-extensibility">dubbo-samples-extensibility&lt;/a>。除了本示例之外，Dubbo 核心仓库 apache/dubbo 以及扩展库 &lt;a href="https://github.com/apache/dubbo-samples/tree/master/10-task/dubbo-samples-extensibility">apache/dubbo-spi-extensions&lt;/a> 中的众多 Protocol 实现，都可以作为扩展参考实现：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-properties" data-lang="properties">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># Dubbo对外支持的常用协议&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>tri&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">org.apache.dubbo.rpc.protocol.tri.TripleProtocol&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="任务详情">任务详情&lt;/h2>
&lt;p>基于现有的&lt;code>dubbo&lt;/code>协议来实现自定义协议&lt;code>edubbo&lt;/code>。&lt;/p>
&lt;h2 id="实现方式">实现方式&lt;/h2>
&lt;p>通过对&lt;code>dubbo&lt;/code>协议进行包装来实现&lt;code>edubbo&lt;/code>协议。&lt;/p>
&lt;h4 id="代码结构">代码结构&lt;/h4>
&lt;h5 id="common">Common&lt;/h5>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-properties" data-lang="properties">&lt;span style="display:flex;">&lt;span>src
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-main
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-java
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-org
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-apache
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-dubbo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-samples
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-extensibility
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-protocol
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-common
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-EnhancedProtocol.java &lt;span style="color:#2aa198">(实现Protocol接口)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="provider">Provider&lt;/h5>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-properties" data-lang="properties">&lt;span style="display:flex;">&lt;span>src
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-main
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-java
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-org
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-apache
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-dubbo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-samples
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-extensibility
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-protocol
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-provider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-ExtensibilityProtocolProviderApplication.java
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-ExtensibilityProtocolServiceImpl.java
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-resources
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-META-INF
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-application.properties &lt;span style="color:#2aa198">(Dubbo Provider配置文件)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-dubbo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-org.apache.dubbo.rpc.Protocol &lt;span style="color:#2aa198">(纯文本文件)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="consumer">Consumer&lt;/h5>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-properties" data-lang="properties">&lt;span style="display:flex;">&lt;span>src
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-main
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-java
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-org
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-apache
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-dubbo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-samples
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-extensibility
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-protocol
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-consumer
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-ExtensibilityProtocolConsumerApplication.java
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-ExtensibilityProtocolConsumerTask.java
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-resources
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-META-INF
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-application.properties &lt;span style="color:#2aa198">(Dubbo Consumer配置文件)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-dubbo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-org.apache.dubbo.rpc.Protocol &lt;span style="color:#2aa198">(纯文本文件)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="代码详情">代码详情&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> org.apache.dubbo.samples.extensibility.protocol.common;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.common.URL;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.Protocol;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.Invoker;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.Exporter;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.ProtocolServer;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.RpcException;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.model.FrameworkModel;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> java.util.List;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">EnhancedProtocol&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> Protocol {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">EnhancedProtocol&lt;/span>(FrameworkModel frameworkModel) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>.protocol &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> DubboProtocol(frameworkModel);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Protocol protocol;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> &lt;span style="color:#268bd2">getDefaultPort&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">this&lt;/span>.protocol.getDefaultPort();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> Exporter&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">export&lt;/span>(Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> invoker) &lt;span style="color:#268bd2">throws&lt;/span> RpcException {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// do something&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">this&lt;/span>.protocol.export(invoker);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">refer&lt;/span>(Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> type, URL url) &lt;span style="color:#268bd2">throws&lt;/span> RpcException {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// do something&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">this&lt;/span>.protocol.refer(type, url);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">destroy&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>.protocol.destroy();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>ProtocolServer&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">getServers&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> protocol.getServers();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="spi配置">SPI配置&lt;/h4>
&lt;h5 id="provider-1">Provider&lt;/h5>
&lt;p>在&lt;code>resources/META-INF/dubbo/org.apache.dubbo.rpc.Protocol&lt;/code>文件中添加如下配置：&lt;/p></description></item><item><title>Registry</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/overview/mannual/java-sdk/tasks/extensibility/registry/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/overview/mannual/java-sdk/tasks/extensibility/registry/</guid><description>&lt;p>在 &lt;a href="https://deploy-preview-3202--dubbo.netlify.app/zh-cn/overview/mannual/java-sdk/tasks/protocols/">服务发现&lt;/a> 一章中，我们了解了 Dubbo 内置的几个核心注册中心实现 &lt;code>Nacos&lt;/code>、&lt;code>Zookeeper&lt;/code> 的使用方式与工作原理。本文讲解如何通过扩展 &lt;code>org.apache.dubbo.registry.client.ServiceDiscovery&lt;/code> 和 &lt;code>org.apache.dubbo.registry.nacos.NacosServiceDiscoveryFactory&lt;/code> SPI，提供自定义的注册中心实现。&lt;/p>
&lt;p>本示例的完整源码请参见 &lt;a href="https://github.com/apache/dubbo-spi-extensions/tree/master/dubbo-configcenter-extensions/dubbo-configcenter-etcd">dubbo-registry-etcd&lt;/a>。除了本示例之外，Dubbo 核心仓库 apache/dubbo 以及扩展库 apache/dubbo-spi-extensions 中的众多注册中心扩展实现，都可以作为扩展参考实现：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-properties" data-lang="properties">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># Dubbo对外支持的常用注册中心实现&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>nacos&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">org.apache.dubbo.registry.nacos.NacosServiceDiscoveryFactory&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>zookeeper&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">org.apache.dubbo.registry.zookeeper.ZookeeperServiceDiscoveryFactory&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="任务详情">任务详情&lt;/h2>
&lt;p>通过扩展 SPI 实现基于的 etcd 注册中心。&lt;/p>
&lt;h2 id="实现方式">实现方式&lt;/h2>
&lt;h4 id="代码详情">代码详情&lt;/h4>
&lt;p>首先，通过继承 &lt;code>AbstractServiceDiscoveryFactory&lt;/code> 实现 &lt;code>ServiceDiscoveryFactory&lt;/code> 接口&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">EtcdServiceDiscoveryFactory&lt;/span> &lt;span style="color:#268bd2">extends&lt;/span> AbstractServiceDiscoveryFactory {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> ServiceDiscovery &lt;span style="color:#268bd2">createDiscovery&lt;/span>(URL registryURL) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> EtcdServiceDiscovery(applicationModel, registryURL);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>EtcdServiceDiscovery&lt;/code> 的一些关键方法与实现如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">EtcdServiceDiscovery&lt;/span> &lt;span style="color:#268bd2">extends&lt;/span> AbstractServiceDiscovery {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> services &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashSet&lt;span style="color:#719e07">&amp;lt;&amp;gt;&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String, InstanceChildListener&lt;span style="color:#719e07">&amp;gt;&lt;/span> childListenerMap &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> EtcdClient etcdClient;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">EtcdServiceDiscovery&lt;/span>(ApplicationModel applicationModel, URL registryURL) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>(applicationModel, registryURL);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> EtcdTransporter etcdTransporter &lt;span style="color:#719e07">=&lt;/span> applicationModel.getExtensionLoader(EtcdTransporter.class).getAdaptiveExtension();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> etcdClient &lt;span style="color:#719e07">=&lt;/span> etcdTransporter.connect(registryURL);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> etcdClient.addStateListener(state &lt;span style="color:#719e07">-&amp;gt;&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (state &lt;span style="color:#719e07">==&lt;/span> StateListener.CONNECTED) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> recover();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">catch&lt;/span> (Exception e) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger.error(e.getMessage(), e);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> });
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>.registryURL &lt;span style="color:#719e07">=&lt;/span> registryURL;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">doRegister&lt;/span>(ServiceInstance serviceInstance) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String path &lt;span style="color:#719e07">=&lt;/span> toPath(serviceInstance);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> etcdClient.putEphemeral(path, &lt;span style="color:#719e07">new&lt;/span> Gson().toJson(serviceInstance));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> services.add(serviceInstance.getServiceName());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">catch&lt;/span> (Throwable e) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RpcException(&lt;span style="color:#2aa198">&amp;#34;Failed to register &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> serviceInstance &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; to etcd &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> etcdClient.getUrl()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, cause: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> (OptionUtil.isProtocolError(e)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">?&lt;/span> &lt;span style="color:#2aa198">&amp;#34;etcd3 registry may not be supported yet or etcd3 registry is not available.&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> : e.getMessage()), e);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">doUnregister&lt;/span>(ServiceInstance serviceInstance) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String path &lt;span style="color:#719e07">=&lt;/span> toPath(serviceInstance);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> etcdClient.delete(path);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> services.remove(serviceInstance.getServiceName());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">catch&lt;/span> (Throwable e) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RpcException(&lt;span style="color:#2aa198">&amp;#34;Failed to unregister &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> serviceInstance &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; to etcd &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> etcdClient.getUrl() &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, cause: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> e.getMessage(), e);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">addServiceInstancesChangedListener&lt;/span>(ServiceInstancesChangedListener listener) &lt;span style="color:#268bd2">throws&lt;/span> NullPointerException, IllegalArgumentException {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> (String serviceName : listener.getServiceNames()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registerServiceWatcher(serviceName, listener);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>ServiceInstance&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">getInstances&lt;/span>(String serviceName) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> children &lt;span style="color:#719e07">=&lt;/span> etcdClient.getChildren(toParentPath(serviceName));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (CollectionUtils.isEmpty(children)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> Collections.emptyList();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>ServiceInstance&lt;span style="color:#719e07">&amp;gt;&lt;/span> list &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&amp;gt;&lt;/span>(children.size());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> (String child : children) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ServiceInstance serviceInstance &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Gson().fromJson(etcdClient.getKVValue(child), DefaultServiceInstance.class);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> list.add(serviceInstance);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> list;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="spi配置">SPI配置&lt;/h4>
&lt;p>在 &lt;code>resources/META-INF/dubbo/org.apache.dubbo.registry.client.ServiceDiscoveryFactory&lt;/code> 文件中添加如下配置：&lt;/p></description></item><item><title>Router</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/overview/mannual/java-sdk/tasks/extensibility/router/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/overview/mannual/java-sdk/tasks/extensibility/router/</guid><description>&lt;p>通过自定义路由，可以根据业务场景的特点来实现特定的路由方式。本示例 router 扩展实现源码请参见 &lt;a href="https://github.com/apache/dubbo-samples/blob/master/10-task/dubbo-samples-extensibility/">dubbo-samples-extensibility&lt;/a>。&lt;/p>
&lt;h2 id="开始之前">开始之前&lt;/h2>
&lt;h2 id="任务详情">任务详情&lt;/h2>
&lt;p>对所有的请求都使用第一提供服务的Provider，如果该Provider下线，则从新选择一个新的Provider。&lt;/p>
&lt;h2 id="实现方式">实现方式&lt;/h2>
&lt;p>在Consumer中自定义一个Router，在Router中将第一次调用的Provider保存下来，如果后续有请求调用且Provider列表中包含第一次调用时使用的Provider，则继续使用第一次调用时使用的Provider，否则重新选去一个Provider。&lt;/p>
&lt;h4 id="代码结构">代码结构&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-properties" data-lang="properties">&lt;span style="display:flex;">&lt;span>src
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-main
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-java
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-org
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-apache
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-dubbo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-samples
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-extensibility
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-router
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-consumer
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-router
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-StickFirstStateRouter.java &lt;span style="color:#2aa198">(实现StateRouter接口)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-StickFirstStateRouterFactory.java &lt;span style="color:#2aa198">(实现StateRouterFactory接口)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-resources
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-META-INF
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-application.properties &lt;span style="color:#2aa198">(Dubbo Consumer配置文件)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-dubbo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-org.apache.dubbo.rpc.cluster.router.state.StateRouterFactory &lt;span style="color:#2aa198">(纯文本文件)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="代码详情">代码详情&lt;/h4>
&lt;ul>
&lt;li>StickFirstStateRouter&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> org.apache.dubbo.samples.extensibility.router.consumer.router;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.common.URL;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.common.config.configcenter.ConfigChangeType;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.common.config.configcenter.ConfigurationListener;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.common.logger.LoggerFactory;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.common.utils.CollectionUtils;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.common.utils.Holder;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.Invocation;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.Invoker;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.RpcException;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.cluster.router.RouterSnapshotNode;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.cluster.router.state.AbstractStateRouter;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.cluster.router.state.BitList;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">StickFirstStateRouter&lt;/span>&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">extends&lt;/span> AbstractStateRouter&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> ConfigurationListener {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">StickFirstStateRouter&lt;/span>(URL url) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>(url);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> String NAME &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;STICK_FIRST_ROUTER&amp;#34;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> ErrorTypeAwareLogger logger &lt;span style="color:#719e07">=&lt;/span> LoggerFactory.getErrorTypeAwareLogger(StickFirstStateRouter.class);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">volatile&lt;/span> BitList&lt;span style="color:#719e07">&amp;lt;&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> firstInvokers;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> BitList&lt;span style="color:#719e07">&amp;lt;&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> &lt;span style="color:#268bd2">doRoute&lt;/span>(BitList&lt;span style="color:#719e07">&amp;lt;&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> invokers, URL url, Invocation invocation, &lt;span style="color:#dc322f">boolean&lt;/span> needToPrintMessage, Holder&lt;span style="color:#719e07">&amp;lt;&lt;/span>RouterSnapshotNode&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> routerSnapshotNodeHolder, Holder&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> messageHolder) &lt;span style="color:#268bd2">throws&lt;/span> RpcException {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (CollectionUtils.isEmpty(invokers)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (needToPrintMessage) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> messageHolder.set(&lt;span style="color:#2aa198">&amp;#34;Directly Return. Reason: Invokers from previous router is empty.&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invokers;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> BitList&lt;span style="color:#719e07">&amp;lt;&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> copy &lt;span style="color:#719e07">=&lt;/span> invokers.clone();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (CollectionUtils.isEmpty(copy)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>.firstInvokers &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> BitList&lt;span style="color:#719e07">&amp;lt;&amp;gt;&lt;/span>(BitList.emptyList());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>.firstInvokers.add(copy.get(0));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>.firstInvokers &lt;span style="color:#719e07">=&lt;/span> copy.and(invokers);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span>(CollectionUtils.isEmpty(&lt;span style="color:#719e07">this&lt;/span>.firstInvokers)){
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>.firstInvokers.add(copy.get(0));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">this&lt;/span>.firstInvokers;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">process&lt;/span>(ConfigChangedEvent event) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (logger.isDebugEnabled()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger.debug(&lt;span style="color:#2aa198">&amp;#34;Notification of tag rule, change type is: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> event.getChangeType() &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, raw rule is:\n &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> event.getContent());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Reset&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (event.getChangeType().equals(ConfigChangeType.DELETED)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>.firstInvokers &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">stop&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>.stop();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>.firstInvokers &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>StickFirstStateRouterFactory&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> org.apache.dubbo.samples.extensibility.router.consumer.router;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.common.URL;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.cluster.router.state.StateRouter;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.cluster.router.state.StateRouterFactory;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">StickFirstStateRouterFactory&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> StateRouterFactory {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> StateRouter&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">getRouter&lt;/span>(Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> interfaceClass, URL url) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> StickFirstStateRouter&lt;span style="color:#719e07">&amp;lt;&amp;gt;&lt;/span>(url);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="spi配置">SPI配置&lt;/h4>
&lt;p>在&lt;code>resources/META-INF/dubbo/org.apache.dubbo.rpc.cluster.router.state.StateRouterFactory&lt;/code>文件中添加如下配置：&lt;/p></description></item></channel></rss>