<?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-3199--dubbo.netlify.app/zh-cn/docsv2.7/dev/</link><description>Recent content in 开发指南 on Apache Dubbo</description><generator>Hugo</generator><language>zh-cn</language><atom:link href="https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docsv2.7/dev/index.xml" rel="self" type="application/rss+xml"/><item><title>源码构建</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docsv2.7/dev/build/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docsv2.7/dev/build/</guid><description>&lt;h2 id="代码签出">代码签出&lt;/h2>
&lt;p>通过以下的这个命令签出最新的项目源码 &lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>git clone https://github.com/apache/dubbo.git
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="分支">分支&lt;/h2>
&lt;p>我们使用 master 作为主干版本的开发，使用分支作为维护版本。可以通过 &lt;a href="https://github.com/apache/dubbo/tags">https://github.com/apache/dubbo/tags&lt;/a> 来查看所有版本的标签。&lt;/p>
&lt;h2 id="构建">构建&lt;/h2>
&lt;p>Dubbo 使用 &lt;a href="http://maven.apache.org">maven&lt;/a> 作为构建工具。&lt;/p>
&lt;p>要求&lt;/p>
&lt;ul>
&lt;li>Java 1.8 以上的版本&lt;/li>
&lt;li>Maven 2.2.1 或者以上的版本&lt;/li>
&lt;/ul>
&lt;p>构建之前需要配置以下的 &lt;code>MAVEN_OPTS&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#b58900">export&lt;/span> &lt;span style="color:#268bd2">MAVEN_OPTS&lt;/span>&lt;span style="color:#719e07">=&lt;/span>-Xmx1024m -XX:MaxPermSize&lt;span style="color:#719e07">=&lt;/span>512m
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>使用以下命令做一次构建&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>mvn clean install
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可以通过以下的构建命令来跳过单元测试&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>mvn install -Dmaven.test.skip
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="构建源代码-jar-包">构建源代码 jar 包&lt;/h2>
&lt;p>通过以下命令以构建 Dubbo 的源代码 jar 包&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>mvn clean source:jar install -Dmaven.test.skip
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>并且修改你的样例项目中的 dubbo 依赖为本地仓库的 SANPSHOT 版本，然后使用远程 debug 来调试 dubbo。&lt;/p>
&lt;h2 id="ide-支持">IDE 支持&lt;/h2>
&lt;p>使用以下命令来生成 IDE 的工程&lt;/p>
&lt;h3 id="intellij-idea">Intellij Idea&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>mvn idea:idea
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="eclipse">eclipse&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>mvn eclipse:eclipse
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在 eclipse 中导入&lt;/p>
&lt;p>首先，需要在 eclipse 中配置 maven 仓库。通过 Preferences -&amp;gt; Java -&amp;gt; Build Path -&amp;gt; Classpath 定义 &lt;code>M2_REPO&lt;/code> 的 classpath 变量指向本地的 maven 仓库。 &lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup>&lt;/p></description></item><item><title>框架设计</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docsv2.7/dev/design/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docsv2.7/dev/design/</guid><description>&lt;h2 id="整体设计">整体设计&lt;/h2>
&lt;p>&lt;img alt="/dev-guide/images/dubbo-framework.jpg" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/dev/dubbo-framework.jpg">&lt;/p>
&lt;p>图例说明：&lt;/p>
&lt;ul>
&lt;li>图中左边淡蓝背景的为服务消费方使用的接口，右边淡绿色背景的为服务提供方使用的接口，位于中轴线上的为双方都用到的接口。&lt;/li>
&lt;li>图中从下至上分为十层，各层均为单向依赖，右边的黑色箭头代表层之间的依赖关系，每一层都可以剥离上层被复用，其中，Service 和 Config 层为 API，其它各层均为 SPI。&lt;/li>
&lt;li>图中绿色小块的为扩展接口，蓝色小块为实现类，图中只显示用于关联各层的实现类。&lt;/li>
&lt;li>图中蓝色虚线为初始化过程，即启动时组装链，红色实线为方法调用过程，即运行时调时链，紫色三角箭头为继承，可以把子类看作父类的同一个节点，线上的文字为调用的方法。&lt;/li>
&lt;/ul>
&lt;h2 id="各层说明">各层说明&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>config 配置层&lt;/strong>：对外配置接口，以 &lt;code>ServiceConfig&lt;/code>, &lt;code>ReferenceConfig&lt;/code> 为中心，可以直接初始化配置类，也可以通过 spring 解析配置生成配置类&lt;/li>
&lt;li>&lt;strong>proxy 服务代理层&lt;/strong>：服务接口透明代理，生成服务的客户端 Stub 和服务器端 Skeleton, 以 &lt;code>ServiceProxy&lt;/code> 为中心，扩展接口为 &lt;code>ProxyFactory&lt;/code>&lt;/li>
&lt;li>&lt;strong>registry 注册中心层&lt;/strong>：封装服务地址的注册与发现，以服务 URL 为中心，扩展接口为 &lt;code>RegistryFactory&lt;/code>, &lt;code>Registry&lt;/code>, &lt;code>RegistryService&lt;/code>&lt;/li>
&lt;li>&lt;strong>cluster 路由层&lt;/strong>：封装多个提供者的路由及负载均衡，并桥接注册中心，以 &lt;code>Invoker&lt;/code> 为中心，扩展接口为 &lt;code>Cluster&lt;/code>, &lt;code>Directory&lt;/code>, &lt;code>Router&lt;/code>, &lt;code>LoadBalance&lt;/code>&lt;/li>
&lt;li>&lt;strong>monitor 监控层&lt;/strong>：RPC 调用次数和调用时间监控，以 &lt;code>Statistics&lt;/code> 为中心，扩展接口为 &lt;code>MonitorFactory&lt;/code>, &lt;code>Monitor&lt;/code>, &lt;code>MonitorService&lt;/code>&lt;/li>
&lt;li>&lt;strong>protocol 远程调用层&lt;/strong>：封装 RPC 调用，以 &lt;code>Invocation&lt;/code>, &lt;code>Result&lt;/code> 为中心，扩展接口为 &lt;code>Protocol&lt;/code>, &lt;code>Invoker&lt;/code>, &lt;code>Exporter&lt;/code>&lt;/li>
&lt;li>&lt;strong>exchange 信息交换层&lt;/strong>：封装请求响应模式，同步转异步，以 &lt;code>Request&lt;/code>, &lt;code>Response&lt;/code> 为中心，扩展接口为 &lt;code>Exchanger&lt;/code>, &lt;code>ExchangeChannel&lt;/code>, &lt;code>ExchangeClient&lt;/code>, &lt;code>ExchangeServer&lt;/code>&lt;/li>
&lt;li>&lt;strong>transport 网络传输层&lt;/strong>：抽象 mina 和 netty 为统一接口，以 &lt;code>Message&lt;/code> 为中心，扩展接口为 &lt;code>Channel&lt;/code>, &lt;code>Transporter&lt;/code>, &lt;code>Client&lt;/code>, &lt;code>Server&lt;/code>, &lt;code>Codec&lt;/code>&lt;/li>
&lt;li>&lt;strong>serialize 数据序列化层&lt;/strong>：可复用的一些工具，扩展接口为 &lt;code>Serialization&lt;/code>, &lt;code>ObjectInput&lt;/code>, &lt;code>ObjectOutput&lt;/code>, &lt;code>ThreadPool&lt;/code>&lt;/li>
&lt;/ul>
&lt;h2 id="关系说明">关系说明&lt;/h2>
&lt;ul>
&lt;li>在 RPC 中，Protocol 是核心层，也就是只要有 Protocol + Invoker + Exporter 就可以完成非透明的 RPC 调用，然后在 Invoker 的主过程上 Filter 拦截点。&lt;/li>
&lt;li>图中的 Consumer 和 Provider 是抽象概念，只是想让看图者更直观的了解哪些类分属于客户端与服务器端，不用 Client 和 Server 的原因是 Dubbo 在很多场景下都使用 Provider, Consumer, Registry, Monitor 划分逻辑拓扑节点，保持统一概念。&lt;/li>
&lt;li>而 Cluster 是外围概念，所以 Cluster 的目的是将多个 Invoker 伪装成一个 Invoker，这样其它人只要关注 Protocol 层 Invoker 即可，加上 Cluster 或者去掉 Cluster 对其它层都不会造成影响，因为只有一个提供者时，是不需要 Cluster 的。&lt;/li>
&lt;li>Proxy 层封装了所有接口的透明化代理，而在其它层都以 Invoker 为中心，只有到了暴露给用户使用时，才用 Proxy 将 Invoker 转成接口，或将接口实现转成 Invoker，也就是去掉 Proxy 层 RPC 是可以 Run 的，只是不那么透明，不那么看起来像调本地服务一样调远程服务。&lt;/li>
&lt;li>而 Remoting 实现是 Dubbo 协议的实现，如果你选择 RMI 协议，整个 Remoting 都不会用上，Remoting 内部再划为 Transport 传输层和 Exchange 信息交换层，Transport 层只负责单向消息传输，是对 Mina, Netty, Grizzly 的抽象，它也可以扩展 UDP 传输，而 Exchange 层是在传输层之上封装了 Request-Response 语义。&lt;/li>
&lt;li>Registry 和 Monitor 实际上不算一层，而是一个独立的节点，只是为了全局概览，用层的方式画在一起。&lt;/li>
&lt;/ul>
&lt;h2 id="模块分包">模块分包&lt;/h2>
&lt;p>&lt;img alt="/dev-guide/images/dubbo-modules.jpg" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/dev/dubbo-modules.jpg">&lt;/p></description></item><item><title>扩展点加载</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docsv2.7/dev/spi/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docsv2.7/dev/spi/</guid><description>&lt;h2 id="扩展点配置">扩展点配置&lt;/h2>
&lt;h3 id="来源">来源：&lt;/h3>
&lt;p>Dubbo 的扩展点加载从 JDK 标准的 SPI (Service Provider Interface) 扩展点发现机制加强而来。&lt;/p>
&lt;p>Dubbo 改进了 JDK 标准的 SPI 的以下问题：&lt;/p>
&lt;ul>
&lt;li>JDK 标准的 SPI 会一次性实例化扩展点所有实现，如果有扩展实现初始化很耗时，但如果没用上也加载，会很浪费资源。&lt;/li>
&lt;li>如果扩展点加载失败，连扩展点的名称都拿不到了。比如：JDK 标准的 ScriptEngine，通过 &lt;code>getName()&lt;/code> 获取脚本类型的名称，但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在，导致 RubyScriptEngine 类加载失败，这个失败原因被吃掉了，和 ruby 对应不起来，当用户执行 ruby 脚本时，会报不支持 ruby，而不是真正失败的原因。&lt;/li>
&lt;li>增加了对扩展点 IoC 和 AOP 的支持，一个扩展点可以直接 setter 注入其它扩展点。&lt;/li>
&lt;/ul>
&lt;h3 id="约定">约定：&lt;/h3>
&lt;p>在扩展类的 jar 包内 &lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>，放置扩展点配置文件 &lt;code>META-INF/dubbo/接口全限定名&lt;/code>，内容为：&lt;code>配置名=扩展实现类全限定名&lt;/code>，多个实现类用换行符分隔。&lt;/p>
&lt;h3 id="示例">示例：&lt;/h3>
&lt;p>以扩展 Dubbo 的协议为例，在协议的实现 jar 包内放置文本文件：&lt;code>META-INF/dubbo/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>xxx&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">com.alibaba.xxx.XxxProtocol&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>实现类内容 &lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> com.alibaba.xxx;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span 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">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">XxxProtocol&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;h3 id="配置模块中的配置">配置模块中的配置&lt;/h3>
&lt;p>Dubbo 配置模块中，扩展点均有对应配置属性或标签，通过配置指定使用哪个扩展实现。比如：&lt;/p></description></item><item><title>实现细节</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docsv2.7/dev/implementation/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docsv2.7/dev/implementation/</guid><description>&lt;h2 id="初始化过程细节">初始化过程细节&lt;/h2>
&lt;h3 id="解析服务">解析服务&lt;/h3>
&lt;p>基于 dubbo.jar 内的 &lt;code>META-INF/spring.handlers&lt;/code> 配置，Spring 在遇到 dubbo 名称空间时，会回调 &lt;code>DubboNamespaceHandler&lt;/code>。&lt;/p>
&lt;p>所有 dubbo 的标签，都统一用 &lt;code>DubboBeanDefinitionParser&lt;/code> 进行解析，基于一对一属性映射，将 XML 标签解析为 Bean 对象。&lt;/p>
&lt;p>在 &lt;code>ServiceConfig.export()&lt;/code> 或 &lt;code>ReferenceConfig.get()&lt;/code> 初始化时，将 Bean 对象转换 URL 格式，所有 Bean 属性转成 URL 的参数。&lt;/p>
&lt;p>然后将 URL 传给 &lt;a href="../impls/protocol">协议扩展点&lt;/a>，基于扩展点的 &lt;a href="../spi">扩展点自适应机制&lt;/a>，根据 URL 的协议头，进行不同协议的服务暴露或引用。&lt;/p>
&lt;h3 id="暴露服务">暴露服务&lt;/h3>
&lt;h4 id="1-只暴露服务端口">1. 只暴露服务端口：&lt;/h4>
&lt;p>在没有注册中心，直接暴露提供者的情况下 &lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>，&lt;code>ServiceConfig&lt;/code> 解析出的 URL 的格式为：
&lt;code>dubbo://service-host/com.foo.FooService?version=1.0.0&lt;/code>。&lt;/p>
&lt;p>基于扩展点自适应机制，通过 URL 的 &lt;code>dubbo://&lt;/code> 协议头识别，直接调用 &lt;code>DubboProtocol&lt;/code>的 &lt;code>export()&lt;/code> 方法，打开服务端口。&lt;/p>
&lt;h4 id="2-向注册中心暴露服务">2. 向注册中心暴露服务：&lt;/h4>
&lt;p>在有注册中心，需要注册提供者地址的情况下 &lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup>，&lt;code>ServiceConfig&lt;/code> 解析出的 URL 的格式为: &lt;code>registry://registry-host/org.apache.dubbo.registry.RegistryService?export=URL.encode(&amp;quot;dubbo://service-host/com.foo.FooService?version=1.0.0&amp;quot;)&lt;/code>，&lt;/p>
&lt;p>基于扩展点自适应机制，通过 URL 的 &lt;code>registry://&lt;/code> 协议头识别，就会调用 &lt;code>RegistryProtocol&lt;/code> 的 &lt;code>export()&lt;/code> 方法，将 &lt;code>export&lt;/code> 参数中的提供者 URL，先注册到注册中心。&lt;/p></description></item><item><title>公共契约</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docsv2.7/dev/contract/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docsv2.7/dev/contract/</guid><description>&lt;h2 id="url">URL&lt;/h2>
&lt;ul>
&lt;li>所有扩展点参数都包含 URL 参数，URL 作为上下文信息贯穿整个扩展点设计体系。&lt;/li>
&lt;li>URL 采用标准格式：&lt;code>protocol://username:password@host:port/path?key=value&amp;amp;key=value&lt;/code>&lt;/li>
&lt;/ul>
&lt;h2 id="日志">日志&lt;/h2>
&lt;ul>
&lt;li>如果不可恢复或需要报警，打印 ERROR 日志。&lt;/li>
&lt;li>如果可恢复异常，或瞬时的状态不一致，打印 WARN 日志。&lt;/li>
&lt;li>正常运行时的中间状态提示，打印 INFO 日志。&lt;/li>
&lt;/ul></description></item><item><title>版本管理</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docsv2.7/dev/release/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docsv2.7/dev/release/</guid><description>&lt;p>&lt;strong>新功能的开发&lt;/strong> 和 &lt;strong>稳定性的提高&lt;/strong> 对产品都很重要。但是添加新功能会影响稳定性，Dubbo 使用如下的版本开发模式来保障两者。&lt;/p>
&lt;h2 id="2-个版本并行开发">2 个版本并行开发&lt;/h2>
&lt;ul>
&lt;li>BugFix 版本：低版本，比如 &lt;code>2.4.x&lt;/code>。是 GA 版本，线上使用的版本，只会 BugFix，升级第三位版本号。&lt;/li>
&lt;li>新功能版本：高版本，比如 &lt;code>2.5.x&lt;/code>。加新功能的版本，会给对新功能有需求的应用试用。&lt;/li>
&lt;/ul>
&lt;p>&lt;code>2.5.x&lt;/code> 的新功能基本稳定后，进入 &lt;code>2.5.x&lt;/code> 试用阶段。找足够多的应用试用 &lt;code>2.5.x&lt;/code> 版本。&lt;/p>
&lt;p>在 &lt;code>2.5.x&lt;/code> 够稳定后：&lt;/p>
&lt;ul>
&lt;li>&lt;code>2.5.x&lt;/code> 成为 GA 版本，只 BugFix，推广使用此版本。如果版本可用，可以推进应用在期望的时间点内升级到 GA 版本。&lt;/li>
&lt;li>&lt;code>2.4.x&lt;/code> 不再开发，应用碰到 Bug 让直接升级。（这个称为“夕阳条款”）&lt;/li>
&lt;li>从 &lt;code>2.5.x&lt;/code> 拉成分支 &lt;code>2.6.0&lt;/code>，作为新功能开发版本。&lt;/li>
&lt;/ul>
&lt;h2 id="优势">优势&lt;/h2>
&lt;ul>
&lt;li>保证 GA 版本是稳定的！因为：
&lt;ul>
&lt;li>只会作 BugFix&lt;/li>
&lt;li>成为 GA 版本前有试用阶段&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>新功能可以在高版本中快速响应，并让应用能试用新功能。&lt;/li>
&lt;li>不会版本过多，导致开发和维护成本剧增&lt;/li>
&lt;/ul>
&lt;h2 id="用户要配合的职责">用户要配合的职责&lt;/h2>
&lt;p>由于开发只会 BugFix GA 版本，所以用户需要积极跟进升级到 GA 版本，以 Fix 发现的问题。&lt;/p>
&lt;p>定期升级版本给用户带来了不安。这是一个假命题，说明如下：&lt;/p>
&lt;ul>
&lt;li>GA 经过一个试用阶段保持稳定。&lt;/li>
&lt;li>GA 版本有 Bug 会火速 Fix&lt;/li>
&lt;li>相对出问题才升级到 GA 版本（可能跨了多个版本）定期升级平摊风险（类似小步快跑）。经历过周期长的大项目的同学会有这样的经历，三方库版本长时间不升级，结果出了问题不得不升级到新版本（跨了多个版本）风险巨大。&lt;/li>
&lt;/ul></description></item><item><title>检查列表</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docsv2.7/dev/checklist/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docsv2.7/dev/checklist/</guid><description>&lt;h2 id="发布前-checklist">发布前 checklist&lt;/h2>
&lt;ul>
&lt;li>jira ticket 过一遍&lt;/li>
&lt;li>svn change list&lt;/li>
&lt;li>ticket 关联 code&lt;/li>
&lt;li>test code&lt;/li>
&lt;li>find bugs&lt;/li>
&lt;/ul>
&lt;h2 id="修复时-checklist">修复时 checklist&lt;/h2>
&lt;ul>
&lt;li>修复代码前先建 ticket&lt;/li>
&lt;li>修复代码前先写测试用例&lt;/li>
&lt;li>需要伙伴检查&lt;/li>
&lt;li>test code(正常流程/异常流程)&lt;/li>
&lt;li>讲一遍逻辑&lt;/li>
&lt;li>契约文档化&lt;/li>
&lt;li>以上内容都写到ticket的评论上&lt;/li>
&lt;li>代码注释写清楚，用中文无妨&lt;/li>
&lt;li>每个版本要有 owner，确保 scope 和 check&lt;/li>
&lt;/ul>
&lt;h2 id="partner-check">Partner Check&lt;/h2>
&lt;ul>
&lt;li>Partner 以用户的方式运行一下功能&lt;/li>
&lt;li>Partner 发现问题、添加测试（集成测试）直到不再复现；Owner 完成实现。（保证两方在Partner Check上的时间投入）&lt;/li>
&lt;li>Owner 向 Partner 讲述一遍实现。&lt;/li>
&lt;/ul></description></item><item><title>编码约定</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docsv2.7/dev/coding/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docsv2.7/dev/coding/</guid><description>&lt;h2 id="代码风格">代码风格&lt;/h2>
&lt;p>Dubbo 的源代码和 JavaDoc 遵循以下的规范：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="http://www.oracle.com/technetwork/java/codeconvtoc-136057.html">Code Conventions for the Java Programming Language&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.oracle.com/technetwork/java/javase/documentation/index-137868.html">How to Write Doc Comments for the Javadoc Tool&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="异常和日志">异常和日志&lt;/h2>
&lt;ul>
&lt;li>尽可能携带完整的上下文信息，比如出错原因，出错的机器地址，调用对方的地址，连的注册中心地址，使用 Dubbo 的版本等。&lt;/li>
&lt;li>尽量将直接原因写在最前面，所有上下文信息，在原因后用键值对显示。&lt;/li>
&lt;li>抛出异常的地方不用打印日志，由最终处理异常者决定打印日志的级别，吃掉异常必需打印日志。&lt;/li>
&lt;li>打印 &lt;code>ERROR&lt;/code> 日志表示需要报警，打印 &lt;code>WARN&lt;/code> 日志表示可以自动恢复，打印 &lt;code>INFO&lt;/code> 表示正常信息或完全不影响运行。&lt;/li>
&lt;li>建议应用方在监控中心配置 &lt;code>ERROR&lt;/code> 日志实时报警，&lt;code>WARN&lt;/code> 日志每周汇总发送通知。&lt;/li>
&lt;li>&lt;code>RpcException&lt;/code> 是 Dubbo 对外的唯一异常类型，所有内部异常，如果要抛出给用户，必须转为 &lt;code>RpcException&lt;/code>。&lt;/li>
&lt;li>&lt;code>RpcException&lt;/code> 不能有子类型，所有类型信息用 ErrorCode 标识，以便保持兼容。&lt;/li>
&lt;/ul>
&lt;h2 id="配置和-url">配置和 URL&lt;/h2>
&lt;ul>
&lt;li>配置对象属性首字母小写，多个单词用驼峰命名 &lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>。&lt;/li>
&lt;li>配置属性全部用小写，多个单词用&amp;quot;-&amp;ldquo;号分隔 &lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup>。&lt;/li>
&lt;li>URL参数全部用小写，多个单词用&amp;rdquo;.&amp;ldquo;号分隔 &lt;sup id="fnref:3">&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref">3&lt;/a>&lt;/sup>。&lt;/li>
&lt;li>尽可能用 URL 传参，不要自定义 Map 或其它上下文格式，配置信息也转成 URL 格式使用。&lt;/li>
&lt;li>尽量减少 URL 嵌套，保持 URL 的简洁性。&lt;/li>
&lt;/ul>
&lt;h2 id="单元和集成测试">单元和集成测试&lt;/h2>
&lt;ul>
&lt;li>单元测试统一用 JUnit 和 EasyMock，集成测试用 TestNG，数据库测试用 DBUnit。&lt;/li>
&lt;li>保持单元测试用例的运行速度，不要将性能和大的集成用例放在单元测试中。&lt;/li>
&lt;li>保持单元测试的每个用例都用 &lt;code>try...finally&lt;/code> 或 &lt;code>tearDown&lt;/code> 释放资源。&lt;/li>
&lt;li>减少 while 循环等待结果的测试用例，对定时器和网络的测试，用以将定时器中的逻辑抽为方法测试。&lt;/li>
&lt;li>对于容错行为的测试，比如 failsafe 的测试，统一用 &lt;code>LogUtil&lt;/code> 断言日志输出。&lt;/li>
&lt;/ul>
&lt;h2 id="扩展点基类与-aop">扩展点基类与 AOP&lt;/h2>
&lt;ul>
&lt;li>AOP 类都命名为 &lt;code>XxxWrapper&lt;/code>，基类都命名为 &lt;code>AbstractXxx&lt;/code>。&lt;/li>
&lt;li>扩展点之间的组合将关系由 AOP 完成，&lt;code>ExtensionLoader&lt;/code> 只负载加载扩展点，包括 AOP 扩展。&lt;/li>
&lt;li>尽量采用 IoC 注入扩展点之间的依赖，不要直接依赖 &lt;code>ExtensionLoader&lt;/code> 的工厂方法。&lt;/li>
&lt;li>尽量采用 AOP 实现扩展点的通用行为，而不要用基类，比如负载均衡之前的 &lt;code>isAvailable&lt;/code> 检查，它是独立于负载均衡之外的，不需要检查的是URL参数关闭。&lt;/li>
&lt;li>对多种相似类型的抽象，用基类实现，比如 RMI, Hessian 等第三方协议都已生成了接口代理，只需将将接口代理转成 &lt;code>Invoker&lt;/code> 即可完成桥接，它们可以用公共基类实现此逻辑。&lt;/li>
&lt;li>基类也是 SPI 的一部分，每个扩展点都应该有方便使用的基类支持。&lt;/li>
&lt;/ul>
&lt;h2 id="模块与分包">模块与分包&lt;/h2>
&lt;ul>
&lt;li>基于复用度分包，总是一起使用的放在同一包下，将接口和基类分成独立模块，大的实现也使用独立模块。&lt;/li>
&lt;li>所有接口都放在模块的根包下，基类放在 support 子包下，不同实现用放在以扩展点名字命名的子包下。&lt;/li>
&lt;li>尽量保持子包依赖父包，而不要反向。&lt;/li>
&lt;/ul>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>Java 约定&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p></description></item><item><title>坏味道</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docsv2.7/dev/code-smell/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docsv2.7/dev/code-smell/</guid><description>&lt;h2 id="url-转换">URL 转换&lt;/h2>
&lt;h3 id="1-点对点暴露和引用服务">1. 点对点暴露和引用服务&lt;/h3>
&lt;p>直接暴露服务：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>EXPORT(dubbo://provider-address/com.xxx.XxxService?version=1.0.0&amp;#34;)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>点对点直连服务：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>REFER(dubbo://provider-address/com.xxx.XxxService?version=1.0.0)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="2-通过注册中心暴露服务">2. 通过注册中心暴露服务&lt;/h3>
&lt;p>向注册中心暴露服务：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span style="display:flex;">&lt;span>EXPORT(registry:&lt;span style="color:#719e07">//&lt;/span>registry&lt;span style="color:#719e07">-&lt;/span>address&lt;span style="color:#719e07">/&lt;/span>org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>registry&lt;span style="color:#719e07">.&lt;/span>RegistrySerevice?registry&lt;span style="color:#719e07">=&lt;/span>dubbo&lt;span style="color:#719e07">&amp;amp;&lt;/span>&lt;span style="color:#719e07">export&lt;/span>&lt;span style="color:#719e07">=&lt;/span>ENCODE(dubbo:&lt;span style="color:#719e07">//&lt;/span>provider&lt;span style="color:#719e07">-&lt;/span>address&lt;span style="color:#719e07">/&lt;/span>com&lt;span style="color:#719e07">.&lt;/span>xxx&lt;span style="color:#719e07">.&lt;/span>XxxService?version&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">1.0&lt;/span>&lt;span style="color:#719e07">.&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>))
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>获取注册中心：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>url.setProtocol(url.getParameter(&amp;#34;registry&amp;#34;, &amp;#34;dubbo&amp;#34;))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>GETREGISTRY(dubbo://registry-address/org.apache.dubbo.registry.RegistrySerevice)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>注册服务地址：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span style="display:flex;">&lt;span>url&lt;span style="color:#719e07">.&lt;/span>getParameterAndDecoded(&lt;span style="color:#2aa198">&amp;#34;export&amp;#34;&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>REGISTER(dubbo:&lt;span style="color:#719e07">//&lt;/span>provider&lt;span style="color:#719e07">-&lt;/span>address&lt;span style="color:#719e07">/&lt;/span>com&lt;span style="color:#719e07">.&lt;/span>xxx&lt;span style="color:#719e07">.&lt;/span>XxxService?version&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">1.0&lt;/span>&lt;span style="color:#719e07">.&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="3-通过注册中心引用服务">3. 通过注册中心引用服务&lt;/h3>
&lt;p>从注册中心订阅服务：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>REFER(registry://registry-address/org.apache.dubbo.registry.RegistrySerevice?registry=dubbo&amp;amp;refer=ENCODE(version=1.0.0))
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>获取注册中心：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>url.setProtocol(url.getParameter(&amp;#34;registry&amp;#34;, &amp;#34;dubbo&amp;#34;))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>GETREGISTRY(dubbo://registry-address/org.apache.dubbo.registry.RegistrySerevice)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>订阅服务地址：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>url.addParameters(url.getParameterAndDecoded(&amp;#34;refer&amp;#34;))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>SUBSCRIBE(dubbo://registry-address/com.xxx.XxxService?version=1.0.0)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>通知服务地址：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>url.addParameters(url.getParameterAndDecoded(&amp;#34;refer&amp;#34;))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>NOTIFY(dubbo://provider-address/com.xxx.XxxService?version=1.0.0)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="4-注册中心推送路由规则">4. 注册中心推送路由规则&lt;/h3>
&lt;p>注册中心路由规则推送：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>NOTIFY(route://registry-address/com.xxx.XxxService?router=script&amp;amp;type=js&amp;amp;rule=ENCODE(function{...}))
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>获取路由器：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>url.setProtocol(url.getParameter(&amp;#34;router&amp;#34;, &amp;#34;script&amp;#34;))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>GETROUTE(script://registry-address/com.xxx.XxxService?type=js&amp;amp;rule=ENCODE(function{...}))
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="5-从文件加载路由规则">5. 从文件加载路由规则&lt;/h3>
&lt;p>从文件加载路由规则：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>GETROUTE(file://path/file.js?router=script)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>获取路由器：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>url.setProtocol(url.getParameter(&amp;#34;router&amp;#34;, &amp;#34;script&amp;#34;)).addParameter(&amp;#34;type&amp;#34;, SUFFIX(file)).addParameter(&amp;#34;rule&amp;#34;, READ(file))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>GETROUTE(script://path/file.js?type=js&amp;amp;rule=ENCODE(function{...}))
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="调用参数">调用参数&lt;/h2>
&lt;ul>
&lt;li>path 服务路径&lt;/li>
&lt;li>group 服务分组&lt;/li>
&lt;li>version 服务版本&lt;/li>
&lt;li>dubbo 使用的 dubbo 版本&lt;/li>
&lt;li>token 验证令牌&lt;/li>
&lt;li>timeout 调用超时&lt;/li>
&lt;/ul>
&lt;h2 id="扩展点的加载">扩展点的加载&lt;/h2>
&lt;h3 id="1-自适应扩展点">1. 自适应扩展点&lt;/h3>
&lt;p>ExtensionLoader 加载扩展点时，会检查扩展点的属性（通过set方法判断），如该属性是扩展点类型，则会注入扩展点对象。因为注入时不能确定使用哪个扩展点（在使用时确定），所以注入的是一个自适应扩展（一个代理）。自适应扩展点调用时，选取一个真正的扩展点，并代理到其上完成调用。Dubbo 是根据调用方法参数（上面有调用哪个扩展点的信息）来选取一个真正的扩展点。&lt;/p></description></item><item><title>技术兼容性测试</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docsv2.7/dev/tck/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docsv2.7/dev/tck/</guid><description>&lt;p>Dubbo 的协议，通讯，序列化，注册中心，负载均策等扩展点，都有多种可选策略，以应对不同应用场景，而我们的测试用例很分散，当用户自己需要加一种新的实现时，总是不确定能否满足扩展点的完整契约。&lt;/p>
&lt;p>所以，我们需要对核心扩展点写 &lt;a href="http://en.wikipedia.org/wiki/Technology_Compatibility_Kit">TCK&lt;/a> (Technology Compatibility Kit)，用户增加一种扩展实现，只需通过 TCK，即可确保与框架的其它部分兼容运行，可以有效提高整体健壮性，也方便第三方扩展者接入，加速开源社区的成熟。&lt;/p>
&lt;p>开源社区的行知同学已着手研究这一块，他的初步想法是借鉴 JBoss 的 CDI-TCK，做一个 Dubbo 的 TCK 基础框架，在此之上实现 Dubbo 的扩展点 TCK 用例。&lt;/p>
&lt;p>参见：http://docs.jboss.org/cdi/tck/reference/1.0.1-Final/html/introduction.html&lt;/p>
&lt;p>如果大家有兴趣，也可以一起研究，和行知一块讨论。&lt;/p>
&lt;h4 id="protocol-tck">Protocol TCK&lt;/h4>
&lt;blockquote>
&lt;p>TODO&lt;/p>
&lt;/blockquote>
&lt;h4 id="registry-tck">Registry TCK&lt;/h4>
&lt;blockquote>
&lt;p>TODO&lt;/p>
&lt;/blockquote></description></item></channel></rss>