<?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/blog/java/demos/</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/blog/java/demos/index.xml" rel="self" type="application/rss+xml"/><item><title>使用 Resilience4j 断路器、限流器、重试、隔离机制保护 Dubbo 应用</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2023/12/14/%E4%BD%BF%E7%94%A8-resilience4j-%E6%96%AD%E8%B7%AF%E5%99%A8%E9%99%90%E6%B5%81%E5%99%A8%E9%87%8D%E8%AF%95%E9%9A%94%E7%A6%BB%E6%9C%BA%E5%88%B6%E4%BF%9D%E6%8A%A4-dubbo-%E5%BA%94%E7%94%A8/</link><pubDate>Thu, 14 Dec 2023 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2023/12/14/%E4%BD%BF%E7%94%A8-resilience4j-%E6%96%AD%E8%B7%AF%E5%99%A8%E9%99%90%E6%B5%81%E5%99%A8%E9%87%8D%E8%AF%95%E9%9A%94%E7%A6%BB%E6%9C%BA%E5%88%B6%E4%BF%9D%E6%8A%A4-dubbo-%E5%BA%94%E7%94%A8/</guid><description>&lt;p>Resilience4j 提供了一组高阶函数（装饰器），包括断路器，限流器，重试，隔离，可以对任何的函数式接口，lambda表达式，或方法的引用进行增强，并且这些装饰器可以进行叠加。这样做的好处是，你可以根据需要选择特定的装饰器进行组合。&lt;/p>
&lt;p>关于 Resilience4j 与 Dubbo 集成的使用示例请参见 &lt;a href="https://github.com/apache/dubbo-samples/tree/master/3-extensions/protocol/dubbo-samples-resilience4j">dubbo-samples-resilience4j&lt;/a>&lt;/p></description></item><item><title>使用 Hystrix 对 Dubbo 服务进行熔断限流保护</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2023/12/14/%E4%BD%BF%E7%94%A8-hystrix-%E5%AF%B9-dubbo-%E6%9C%8D%E5%8A%A1%E8%BF%9B%E8%A1%8C%E7%86%94%E6%96%AD%E9%99%90%E6%B5%81%E4%BF%9D%E6%8A%A4/</link><pubDate>Thu, 14 Dec 2023 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2023/12/14/%E4%BD%BF%E7%94%A8-hystrix-%E5%AF%B9-dubbo-%E6%9C%8D%E5%8A%A1%E8%BF%9B%E8%A1%8C%E7%86%94%E6%96%AD%E9%99%90%E6%B5%81%E4%BF%9D%E6%8A%A4/</guid><description>&lt;h2 id="背景">背景&lt;/h2>
&lt;p>Hystrix 旨在通过控制那些访问远程系统、服务和第三方库的节点，从而对延迟和故障提供更强大的容错能力。Hystrix具备拥有回退机制和断路器功能的线程和信号隔离，请求缓存和请求打包，以及监控和配置等功能。&lt;/p>
&lt;p>本文介绍在spring应用里，怎么把 Dubbo 和 Hystrix 结合起来使用。&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/Netflix/Hystrix">https://github.com/Netflix/Hystrix&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/apache/dubbo">https://github.com/apache/dubbo&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="spring-boot应用">Spring Boot应用&lt;/h2>
&lt;p>Demo 地址： &lt;a href="https://github.com/dubbo/dubbo-samples/tree/master/4-governance/dubbo-samples-spring-boot-hystrix">https://github.com/dubbo/dubbo-samples/tree/master/4-governance/dubbo-samples-spring-boot-hystrix&lt;/a>&lt;/p>
&lt;h3 id="生成dubbo集成spring-boot的应用">生成dubbo集成spring boot的应用&lt;/h3>
&lt;p>对于不熟悉dubbo 集成spring boot应用的同学，可以在这里直接生成dubbo + spring boot的工程： &lt;a href="http://start.dubbo.apache.org/bootstrap.html/">http://start.dubbo.apache.org/bootstrap.html/&lt;/a>&lt;/p>
&lt;h3 id="配置spring-cloud-starter-netflix-hystrix">配置spring-cloud-starter-netflix-hystrix&lt;/h3>
&lt;p>spring boot官方提供了对hystrix的集成，直接在pom.xml里加入依赖：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>org.springframework.cloud&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>spring-cloud-starter-netflix-hystrix&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;version&amp;gt;&lt;/span>1.4.4.RELEASE&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>然后在Application类上增加&lt;code>@EnableHystrix&lt;/code>来启用hystrix starter：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@SpringBootApplication&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@EnableHystrix&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ProviderApplication&lt;/span> {
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="配置provider端">配置Provider端&lt;/h3>
&lt;p>在Dubbo的Provider上增加&lt;code>@HystrixCommand&lt;/code>配置，这样子调用就会经过Hystrix代理。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Service&lt;/span>(version &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;1.0.0&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">HelloServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> HelloService {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@HystrixCommand&lt;/span>(commandProperties &lt;span style="color:#719e07">=&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@HystrixProperty&lt;/span>(name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;circuitBreaker.requestVolumeThreshold&amp;#34;&lt;/span>, value &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;10&amp;#34;&lt;/span>),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@HystrixProperty&lt;/span>(name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;execution.isolation.thread.timeoutInMilliseconds&amp;#34;&lt;/span>, value &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;2000&amp;#34;&lt;/span>) })
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">sayHello&lt;/span>(String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// System.out.println(&amp;#34;async provider received: &amp;#34; + name);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// return &amp;#34;annotation: hello, &amp;#34; + name;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RuntimeException(&lt;span style="color:#2aa198">&amp;#34;Exception to show hystrix enabled.&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="配置consumer端">配置Consumer端&lt;/h3>
&lt;p>对于Consumer端，则可以增加一层method调用，并在method上配置&lt;code>@HystrixCommand&lt;/code>。当调用出错时，会走到&lt;code>fallbackMethod = &amp;quot;reliable&amp;quot;&lt;/code>的调用里。&lt;/p></description></item><item><title>Dubbo 连接异构微服务体系 - 多协议&amp;多注册中心</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2023/01/05/dubbo-%E8%BF%9E%E6%8E%A5%E5%BC%82%E6%9E%84%E5%BE%AE%E6%9C%8D%E5%8A%A1%E4%BD%93%E7%B3%BB-%E5%A4%9A%E5%8D%8F%E8%AE%AE%E5%A4%9A%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83/</link><pubDate>Thu, 05 Jan 2023 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2023/01/05/dubbo-%E8%BF%9E%E6%8E%A5%E5%BC%82%E6%9E%84%E5%BE%AE%E6%9C%8D%E5%8A%A1%E4%BD%93%E7%B3%BB-%E5%A4%9A%E5%8D%8F%E8%AE%AE%E5%A4%9A%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83/</guid><description>&lt;p>从编程开发的角度来说，Dubbo 首先是一款 RPC 服务框架，它最大的优势在于提供了面向接口代理的服务编程模型，对开发者屏蔽了底层的远程通信细节。同时 Dubbo 也是一款服务治理框架，它为分布式部署的微服务提供了服务发现、流量调度等服务治理解决方案。&lt;/p>
&lt;p>在这篇文章中，我们将以以上基础能力为背景，尝试突破 Dubbo 体系自身，探索如何利用 Dubbo 对多协议、多服务发现模型的支持，来实现异构微服务体系间的互联互通。在实际业务场景中，这可以用来解决异构技术体系共存场景下的通信问题，帮助公司实现在异构技术体系间作平滑迁移，解决大规模跨区域、多集群部署场景的地址发现及流量调度等问题。&lt;/p>
&lt;h2 id="面向接口代理的透明服务开发框架">面向接口代理的透明服务开发框架&lt;/h2>
&lt;p>我们还是从 &lt;strong>Dubbo 是一个微服务开发框架&lt;/strong> 这个大家熟知的概念开始。就像 Spring 是开发 Java 应用的基础框架一样，我们经常会选用 Dubbo 作为开发微服务业的基础框架。 Dubbo 框架的最大优势我认为就在其面向接口的编程模型，使得开发远程服务调用就像开发本地服务一样（以 Java 语言为例）：&lt;/p>
&lt;ol>
&lt;li>服务定义&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">GreetingsService&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHi&lt;/span>(String name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>消费方调用服务&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 和调用本地服务一样，完全透明。&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Reference&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> GreetingService greetingService;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">doSayHello&lt;/span>(String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> greetingService.sayHi(&lt;span style="color:#2aa198">&amp;#34;Hello world!&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>下图是 Dubbo 的基本工作原理图，服务提供者与服务消费者之间通过注册中心协调地址，通过约定的协议实现数据交换。&lt;/p>
&lt;p>&lt;img alt="Dubbo basic work flow" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/2023/01/protocols/img.png">&lt;/p>
&lt;h2 id="同构异构微服务体系面临的问题">同构/异构微服务体系面临的问题&lt;/h2>
&lt;p>关于 Dubbo 协议本身及其服务治理相关功能细节并不是本文的重点，我们今天将从一个更高的层次，来看看公司内部构建微服务体系所面的挑战，以及 Dubbo 能为架构选型和迁移等提供哪些解决思路。&lt;/p>
&lt;p>一个公司内部的微服务可能都是基于某一个相同的服务框架开发的，比如说 Dubbo，对于这样的架构，我们称之为是&lt;strong>同构的微服务体系&lt;/strong>；而有些公司的微服务可能是使用多个不同的服务框架所建设，我们称之为&lt;strong>异构的微服务体系&lt;/strong>，多个不同技术栈微服务体系的共存在大型组织内还是非常普遍的，造成这种局面可能有很多原因。比如，可能是遗留系统带来的，也可能是公司正在做技术栈迁移，或者就是不同业务部门为了满足各自特殊需求而做的独立选型（这也意味着异构微服务体系的长期共存）。&lt;/p>
&lt;p>&lt;strong>1. 异构微服务体系共存&lt;/strong>&lt;/p>
&lt;p>我们很容易想到的一个挑战是：**不同的体系间通常是使用不同的 RPC 通信协议、部署独立的注册中心集群，面对这种多协议、多注册中心集群的场景，要如何实现相互之间透明的地址发现和透明的 RPC 调用？**如果我们什么都不做，那么每个微服务体系就只能感知到自己体系内的服务状态，流量也在各自的体系内封闭。而要做到从体系 A 平滑的迁移到体系 B，或者想长期的保持公司内部多个体系的共存，则解决不同体系间的互联互通，实现流量的透明调度将是非常重要的环节。&lt;/p></description></item><item><title>Proxyless Mesh在Dubbo中的实践</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2022/09/05/proxyless-mesh%E5%9C%A8dubbo%E4%B8%AD%E7%9A%84%E5%AE%9E%E8%B7%B5/</link><pubDate>Mon, 05 Sep 2022 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2022/09/05/proxyless-mesh%E5%9C%A8dubbo%E4%B8%AD%E7%9A%84%E5%AE%9E%E8%B7%B5/</guid><description>&lt;h2 id="背景">背景&lt;/h2>
&lt;p>随着 Dubbo 3.1 的 release，Dubbo 在云原生的路上又迈出了重要的一步。在这个版本中添加了 Proxyless Mesh 的新特性，Dubbo Proxyless Mesh 直接实现 xDS 协议解析，
实现 Dubbo 与 Control Plane 的直接通信，进而实现控制面对流量管控、服务治理、可观测性、安全等的统一管控，规避 Sidecar 模式带来的性能损耗与部署架构复杂性。&lt;/p>
&lt;h2 id="什么是service-mesh">什么是Service Mesh&lt;/h2>
&lt;p>Service Mesh 又译作 “服务网格”，作为服务间通信的基础设施层。Buoyant 公司的 CEO Willian Morgan 在他的这篇文章 &lt;a href="https://linkerd.io/2017/04/25/whats-a-service-mesh-and-why-do-i-need-one/">WHAT’S A Service Mesh? AND WHY DO I NEED ONE? &lt;/a>
中解释了什么是 Service Mesh，为什么云原生应用需要 Service Mesh。&lt;/p>
&lt;p>&lt;strong>下面是 Willian Morgan 对 Service Mesh 的解释。&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>A Service Mesh is a dedicated infrastructure layer for handling service-to-service communication. 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>It’s responsible for the reliable delivery of requests through the complex topology of services 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>that comprise a modern, cloud native application. In practice, the Service Mesh is typically implemented 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>as an array of lightweight network proxies that are deployed alongside application code, without the 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>application needing to be aware.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>翻译成中文&lt;/strong>&lt;/p></description></item><item><title>Rest 协议</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2022/07/26/rest-%E5%8D%8F%E8%AE%AE/</link><pubDate>Tue, 26 Jul 2022 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2022/07/26/rest-%E5%8D%8F%E8%AE%AE/</guid><description>&lt;h1 id="dubborestprotocol设计文档">Dubbo RestProtocol 设计文档&lt;/h1>
&lt;h2 id="原版本dubborest">原版本dubbo rest&lt;/h2>
&lt;p>consumer&lt;/p>
&lt;p>restClient支持 依赖resteasy 不支持spring mvc &lt;/p>
&lt;p>provider(较重)&lt;/p>
&lt;p>依赖web container   (tomcat,jetty，)servlet 模式，jaxrs netty server&lt;/p>
&lt;h3 id="改版dubborest">改版dubbo rest &lt;/h3>
&lt;p>方向：&lt;/p>
&lt;p>更加轻量，具有dubbo风格的rest，微服务体系互通（Springcloud Alibaba）&lt;/p>
&lt;p>1.注解解析&lt;/p>
&lt;p>2.报文编解码&lt;/p>
&lt;p>3.restClient&lt;/p>
&lt;p>4.restServer(netty)&lt;/p>
&lt;p>支持程度：&lt;/p>
&lt;p>content-type   text json xml form(后续会扩展)&lt;/p>
&lt;p>注解&lt;/p>
&lt;p>param,header,body,pathvariable （spring mvc &amp;amp; resteasy）&lt;/p>
&lt;h2 id="http协议报文">Http 协议报文&lt;/h2>
&lt;pre>&lt;code>POST /test/path? HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-type: application/json


{&amp;quot;name&amp;quot;:&amp;quot;dubbo&amp;quot;,&amp;quot;age&amp;quot;:10,&amp;quot;address&amp;quot;:&amp;quot;hangzhou&amp;quot;}
&lt;/code>&lt;/pre>
&lt;h3 id="dubbohttpheader">dubbo http(header)&lt;/h3>
&lt;pre>&lt;code>// service key header
path: com.demo.TestInterface
group: demo
port: 80
version: 1.0.0

// 保证长连接
Keep-Alive,Connection: keep-alive
Keep-alive: 60

// RPCContext Attachment
userId: 123456
&lt;/code>&lt;/pre>
&lt;h2 id="目前支持粒度">目前支持粒度：&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th style="text-align: left">数据位置&lt;/th>
 &lt;th style="text-align: left">content-type&lt;/th>
 &lt;th style="text-align: left">spring注解&lt;/th>
 &lt;th style="text-align: left">resteasy注解&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td style="text-align: left">body&lt;/td>
 &lt;td style="text-align: left">无要求&lt;/td>
 &lt;td style="text-align: left">ReuqestBody&lt;/td>
 &lt;td style="text-align: left"> 无注解即为body&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">querystring(?test=demo)&lt;/td>
 &lt;td style="text-align: left">无要求&lt;/td>
 &lt;td style="text-align: left">RequestParam&lt;/td>
 &lt;td style="text-align: left">QueryParam&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">header&lt;/td>
 &lt;td style="text-align: left">无要求&lt;/td>
 &lt;td style="text-align: left">RequestHeader&lt;/td>
 &lt;td style="text-align: left">PathParam&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">form&lt;/td>
 &lt;td style="text-align: left">application/x-www-form-urlencoded&lt;/td>
 &lt;td style="text-align: left">RequestParam ReuqestBody&lt;/td>
 &lt;td style="text-align: left">FormParam&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">path&lt;/td>
 &lt;td style="text-align: left">无要求&lt;/td>
 &lt;td style="text-align: left">PathVariable&lt;/td>
 &lt;td style="text-align: left">PathParam&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">method&lt;/td>
 &lt;td style="text-align: left">无要求&lt;/td>
 &lt;td style="text-align: left">PostMapping GetMapping&lt;/td>
 &lt;td style="text-align: left">GET POST&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">url&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">PostMapping GetMapping path属性&lt;/td>
 &lt;td style="text-align: left">Path&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">content-type&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">PostMapping GetMapping consumers属性&lt;/td>
 &lt;td style="text-align: left">Consumers&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">Accept&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">PostMapping GetMapping produces属性&lt;/td>
 &lt;td style="text-align: left">Produces&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="rest注解解析servicerestmetadataresolver">rest注解解析（ServiceRestMetadataResolver）&lt;/h2>
&lt;pre>&lt;code>JAXRSServiceRestMetadataResolver

SpringMvcServiceRestMetadataResolver
&lt;/code>&lt;/pre>
&lt;p>ServiceRestMetadata&lt;/p></description></item><item><title>Dubbo-Api-Docs -- Apache Dubbo文档展示&amp;测试工具</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2020/12/22/dubbo-api-docs--apache-dubbo%E6%96%87%E6%A1%A3%E5%B1%95%E7%A4%BA%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7/</link><pubDate>Tue, 22 Dec 2020 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2020/12/22/dubbo-api-docs--apache-dubbo%E6%96%87%E6%A1%A3%E5%B1%95%E7%A4%BA%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7/</guid><description>&lt;h1 id="dubbo-api-docs">Dubbo-Api-Docs&lt;/h1>
&lt;h2 id="背景">背景&lt;/h2>
&lt;p>Swagger 是一个规范和完整的前端框架,用于生成,描述,调用和可视化 RESTful 风格的 Web 服务.
Swagger 规范也逐渐发展成为了 OpenAPI 规范.&lt;/p>
&lt;p>Springfox 是一个集成了Swagger,基于 Sring MVC/Spring Webflux 实现的一个 Swagger 描述文件生成框架,通过使用它定义的
一些描述接口的注解自动生成Swagger的描述文件, 使 Swagger 能够展示并调用接口.&lt;/p>
&lt;p>相信很多人都听说和使用过Swagger和Springfox, 这里就不再赘述了.&lt;/p>
&lt;p>Dubbo-Admin中有接口测试功能,但是缺少接口描述的文档,所以该测试功能比较适合接口开发人员用于测试接口.而其他人想要使用该功能就必须
先通过接口开发者编写的文档或者其他方式了解清楚接口信息才能使用该功能测试接口.
Dubbo这边有没有集合文档展示和测试功能,能不用写文档就能把接口直接给调用方,类似Swagger/Springfox的工具呢?
之前做过一些调研,找到一些类似的工具:&lt;/p>
&lt;ul>
&lt;li>有些是基于Springfox做的,直接一个文本域放JSON, 与目前Admin中的测试功能大同小异&lt;/li>
&lt;li>有些是直接基于Swagger的Java版OpenApi规范生成工具做的,能把一些基础数据类型的简单参数作为表单项展示&lt;/li>
&lt;/ul>
&lt;p>它们都有一个共同点: 会把你的提供者变为Web项目. 当然有些提供者是通过web容器加载启动的,甚至也有和web工程在一起的,那就无所谓了.
但也有非web的提供者. 为了文档我得把它变为web项目吗?(还要引入一堆Web框架的依赖?比如Spring MVC)或者说生产环境打包时删除它的引用
和代码里的相关注解? 有没有简单点的方式呢?&lt;/p>
&lt;p>OpenAPI中没有RPC的规范,Swagger是OpenAPI的实现,所以也不支持RPC相关调用.Springfox是通过Swagger实现的 RESTful API的工具,
而RESTful又是基于Web的,Dubbo没法直接使用.我们最终选择了自己实现:&lt;/p>
&lt;ul>
&lt;li>提供一些描述接口信息的简单注解&lt;/li>
&lt;li>在提供者启动时解析注解并缓存解析结果&lt;/li>
&lt;li>在提供者增加几个Dubbo-Api-Docs使用的获取接口信息的接口&lt;/li>
&lt;li>在Dubbo Admin侧通过Dubbo泛化调用实现Http方式调用Dubbo接口的网关&lt;/li>
&lt;li>在Dubbo Admin侧实现接口信息展示和调用接口功能&lt;/li>
&lt;li>下列情况中的参数直接展示为表单项,其他的展示为JSON:
&lt;ul>
&lt;li>方法参数为基础数据类型的&lt;/li>
&lt;li>方法参数为一个Bean,Bena中属性为基础数据类型的&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>很少的第三方依赖,甚至大部分都是你项目里本身就使用的&lt;/li>
&lt;li>可以通过profile决定是否加载, 打包时简单的修改profile就能区分生产和测试,甚至profile你本来就使用了&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>今天,我很高兴的宣布: Dubbo 用户也可以享受类似Swagger的体验了 &amp;ndash; Dubbo-Api-Docs发布了.&lt;/p>
&lt;/blockquote>
&lt;h2 id="简介">简介&lt;/h2>
&lt;p>Dubbo-Api-Docs 是一个展示dubbo接口文档,测试接口的工具.&lt;/p>
&lt;p>使用 Dubbo-Api-Docs 分为两个主要步骤:&lt;/p>
&lt;ol>
&lt;li>在dubbo项目引入Dubbo-Api-Docs 相关jar包,并增加类似Swagger的注解.&lt;/li>
&lt;li>在 Dubbo-Admin 中查看接口描述并测试.&lt;/li>
&lt;/ol>
&lt;p>通过以上两个步骤即可享受类似Swagger的体验, 并且可以在生产环境中关闭Dubbo-Api-Docs的扫描.&lt;/p></description></item><item><title>Dubbo测试验证</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/12/02/dubbo%E6%B5%8B%E8%AF%95%E9%AA%8C%E8%AF%81/</link><pubDate>Mon, 02 Dec 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/12/02/dubbo%E6%B5%8B%E8%AF%95%E9%AA%8C%E8%AF%81/</guid><description>&lt;p>除了线上常规的使用场景以外，我们在日常使用中还需要一些特定的使用方式，比如对正在开发的功能进行验证测试，比如单独调用某台机器的服务，这篇文章就来介绍一下这些场景下的使用方式。&lt;/p>
&lt;h3 id="只订阅">只订阅&lt;/h3>
&lt;p>为方便开发测试，经常会在线下共用一个所有服务可用的注册中心，这时，如果一个正在开发中的服务提供者注册，可能会影响消费者不能正常运行。&lt;/p>
&lt;p>可以让服务提供者开发方，只订阅服务(开发的服务可能依赖其它服务)，而不注册正在开发的服务，通过直连测试正在开发的服务。 &lt;br>
&lt;img alt="subscribe-only" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/subscribe-only.jpg">
禁用注册配置&lt;/p>
&lt;pre>&lt;code>&amp;lt;dubbo:registry address=&amp;quot;10.20.153.10:9090&amp;quot; register=&amp;quot;false&amp;quot; /&amp;gt;
&lt;/code>&lt;/pre>
&lt;p>或者&lt;/p>
&lt;pre>&lt;code>&amp;lt;dubbo:registry address=&amp;quot;10.20.153.10:9090?register=false&amp;quot; /&amp;gt;
&lt;/code>&lt;/pre>
&lt;h3 id="指定ip调用">指定IP调用&lt;/h3>
&lt;p>在开发及测试环境下，经常需要绕过注册中心，只测试指定服务提供者，这时候可能需要点对点直连，点对点直联方式，将以服务接口为单位，忽略注册中心的提供者列表，A 接口配置点对点，不影响 B 接口从注册中心获取列表&lt;br>
&lt;img alt="subscribe-only" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/dubbo-directly.jpg">&lt;/p>
&lt;p>可以通过以下几种配置来指定IP调用&lt;/p>
&lt;ul>
&lt;li>XML 配置： 如果是线上需求需要点对点，可在 &lt;code>&amp;lt;dubbo:reference&amp;gt;&lt;/code> 中配置 url 指向提供者，将绕过注册中心，多个地址用分号隔开，配置如下：
&lt;code>&amp;lt;dubbo:reference id=&amp;quot;xxxService&amp;quot; interface=&amp;quot;com.alibaba.xxx.XxxService&amp;quot; url=&amp;quot;dubbo://localhost:20890&amp;quot; /&amp;gt;&lt;/code>&lt;/li>
&lt;li>通过-D参数指定： 在 JVM 启动参数中加入-D参数映射服务地址，如：&lt;code>java -Dcom.alibaba.xxx.XxxService=dubbo://localhost:20890&lt;/code>&lt;/li>
&lt;li>通过文件映射: 如果服务比较多，也可以用文件映射，用 -Ddubbo.resolve.file 指定映射文件路径，此配置优先级高于 &lt;code>&amp;lt;dubbo:reference&amp;gt;&lt;/code> 中的配置，如：
&lt;code>java -Ddubbo.resolve.file=xxx.properties&lt;/code>&lt;br>
然后在映射文件 xxx.properties 中加入配置，其中 key 为服务名，value 为服务提供者 URL：&lt;code>com.alibaba.xxx.XxxService=dubbo://localhost:20890&lt;/code>&lt;/li>
&lt;/ul>
&lt;h3 id="回声测试">回声测试&lt;/h3>
&lt;h4 id="使用方式">使用方式&lt;/h4>
&lt;p>回声测试用于检测服务是否可用，回声测试按照正常请求流程执行，能够测试整个调用是否通畅，可用于监控。&lt;/p>
&lt;p>所有服务自动实现 EchoService 接口，只需将任意服务引用强制转型为 EchoService，即可使用。&lt;/p>
&lt;p>Spring 配置：&lt;/p>
&lt;pre>&lt;code>&amp;lt;dubbo:reference id=&amp;quot;memberService&amp;quot; interface=&amp;quot;com.xxx.MemberService&amp;quot; /&amp;gt;
&lt;/code>&lt;/pre>
&lt;p>代码：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>// 远程服务引用
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>MemberService memberService = ctx.getBean(&amp;#34;memberService&amp;#34;); 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>EchoService echoService = (EchoService) memberService; // 强制转型为EchoService
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>// 回声测试可用性
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>String status = echoService.$echo(&amp;#34;OK&amp;#34;); 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>assert(status.equals(&amp;#34;OK&amp;#34;));
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="实现原理">实现原理&lt;/h4>
&lt;p>我们在实现，注册服务的时候，并没有配置EchoService这个接口，为什么可以直接使用呢？原来是Dubbo在生成proxy的时候，已经实现了&lt;code>EchoService这个接口&lt;/code>&lt;/p></description></item><item><title>Dubbo 在 Service Mesh 下的思考和方案</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/11/30/dubbo-%E5%9C%A8-service-mesh-%E4%B8%8B%E7%9A%84%E6%80%9D%E8%80%83%E5%92%8C%E6%96%B9%E6%A1%88/</link><pubDate>Sat, 30 Nov 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/11/30/dubbo-%E5%9C%A8-service-mesh-%E4%B8%8B%E7%9A%84%E6%80%9D%E8%80%83%E5%92%8C%E6%96%B9%E6%A1%88/</guid><description>&lt;h2 id="开头">开头&lt;/h2>
&lt;p>Service Mesh这个“热”词是2016年9月被“造”出来，而今年2018年更是被称为service Mesh的关键之年，各家大公司都希望能在这个思潮下领先一步。今天我也分享阿里中间件在这方面的观点，思考和实践。考虑到有些人没了解过Dubbo(集团内以HSF为主)和Servicemesh，先简单介绍下这两个词。Dubbo应该是国内最受欢迎的远程服务框架，在Github上有超过2w的star数，也是阿里分布式架构互联互通的核心所在。跟Dubbo一样，servicemesh也是面向服务互联互通这一问题域，是云原生技术栈的核心之一；大家可以简单理解service mesh就是云原生组织定义的微服务架构解决理念。Dubbo是实现框架，融入service mesh理念就是我们今天分享的。&lt;/p>
&lt;h2 id="现状和挑战">现状和挑战&lt;/h2>
&lt;p>&lt;img alt="1.png | center | 826x206" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/dubbomesh/1.png">&lt;/p>
&lt;p>当前Dubbo支撑的阿里分布式应用内支撑万级别的应用数，运行在20多万的服务器实例上，每天调用量是万亿级别，这应该是国内最大的分布式应用集群。&lt;/p>
&lt;p>挑战主要来自三方面&lt;/p>
&lt;ul>
&lt;li>首先， 数以万计的应用意味着有以十万级的服务，理顺错综复杂的服务拓扑关系，甚至及时诊断某个异常调用链路，需要考虑海量数据的拉取分析，是非常有挑战的，阿里通过EagleEye鹰眼链路系统提供可观察性和治理能力来解决；&lt;/li>
&lt;li>第二个挑战是机房级别容灾，阿里的机房是分布在天南海北，大家可以想象横跨数千公里的网络延迟会造成服务互通很大的影响，所以在保证一定恢复时间和一定数据容错的情况下做异地多活是有巨大挑战，阿里通过支持异地多活的单元化架构解决。&lt;/li>
&lt;li>第三个挑战是阿里业务众多，尤其像阿里生态中的高德，UC，优酷等所使用的开发语言跟淘系Java是不一样的，比如PHP，C，Nodejs，Dart等，要维护多个版本并保证各版本具有同样的功能是成本比较高的；这个挑战在云原生的新一代理念下更具挑战，毕竟。今天主题跟第三个挑战是息息相关，能解决一定的问题。&lt;/li>
&lt;/ul>
&lt;p>这里讲个大鱼吃小鱼的故事来简单理解下云原生：软件会吃掉这个世界，也就是信息化不可避免，而开源会吃掉软件，最终云原生会吃掉开源。这正代表了云原生理念的颠覆性，从商业软件到开源到云原生，环环相套，以体系化和层次化的方式推荐各个方面的开源方案和标准，这会极大降低企业级架构服务的技术门槛，是企业信息化之路的一大利好，当然也是进化方向。这个故事跟今天的主题&amp;ndash;开发者定义软件未来，是非常契合，也就是说这个趋势至少在企业级软件服务领域正在发生。云原生：Cloud Native is Patterns with A complete and trusted tool kit for modern architectures。&lt;/p>
&lt;p>&lt;span data-type="color" style="color:white">Service Mesh的典型方案&lt;/span>&lt;/p>
&lt;h2 id="service-mesh的典型方案">Service Mesh的典型方案&lt;/h2>
&lt;p>&lt;img alt="2.png | center | 826x206" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/dubbomesh/2.png">&lt;/p>
&lt;p>讲完故事，回到servicemesh。&lt;/p>
&lt;p>传统形态下SDK代表着一个特定语言的库，由应用和微服务框架共处一进程内，在发布升级中共享生命周期。比较典型的代表是Twitter的finagle，Google的stubby/grpc，阿里巴巴的HSF/Dubbo.&lt;/p>
&lt;p>Serviemesh下推荐是右边Sidecar方案，Sidecar方案没有引入新的功能，只是改变了原有功能的位置，以独立的应用来存在，大家可以暂时以nginx来理解其网络代理能力也可以。&lt;/p>
&lt;p>在这张图中希望大家关注两个信息， 1）所有的sidecar形成逻辑网络被称为数据面，是业务服务的链路中是强依赖节点，承载了业务数据互联互通的基础；传统的ops管控服务被称为控制面，这部分跟传统是大同小异。 2）在sidecar形态下，网络会增加两跳，即应用与sidecar之间，他们之间的数据互通也是基于协议规范。后面会详细讲。&lt;/p>
&lt;h2 id="sidecar模式的优劣">Sidecar模式的优劣&lt;/h2>
&lt;p>&lt;img alt="3.png | center | 826x206" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/dubbomesh/3.png">&lt;/p>
&lt;p>接下来从开发和运维两个阶段来分开比较。&lt;/p>
&lt;ul>
&lt;li>多语言支持方面，既然sidecar是独立应用，用最合适的一种语言开发完成即可，就避免了需要针对不同语言的应用场景做不同的版本开发。当前阿里选择基于C语言的Envoy做二次开发来追求最小的footprint和性能，当然也曾经历一些弯路，比如曾经用Java开发过一个sidecar，但最终由于引入JRE体量大和GC带来的抖动等问题证明不可行。有必要强调的是：这里说的是sidecar自身开发现在避免了多语言多版本的问题，而真要支持任意服务自由采用任意语言实现这一理想，是需要站在从业务到数据面再到业务的整个链路上的数据交互做思考。&lt;/li>
&lt;li>性能方面，sidecar情形下由于会增加两跳，这两跳是业务应用与sidecar的两个进程之间的调用，这是本机，即便是经过优化，也是会增加进程切换以及数据转换的开销。经过我们的优化测试，在正常的业务访问下，相比SDK形态下最多增加1毫秒的开销，这在大多数业务情形下是基本无感知无影响。&lt;/li>
&lt;li>再看运维阶段的比较，一般SDK形态的服务框架都是只关心开发的诉求，对于如何运维都是不关心，而软件生命周期中运维是最长的，如何从中间件角度解决更多的运维问题是非常有意义的。阿里的中间件经常需要升级，以库的形式升级时就需要业务方应用重新打包，这个推动业务方变更的方式是比较被动，而且周期很长。&lt;/li>
&lt;li>当以镜像为基本原子单位进行发布部署时，阿里的中间件SDK体量大概是200兆，需要与业务一起打包，这样在业务应用升级时让分发的包就显得笨重，时效性相比sidecar形态就差一截。&lt;/li>
&lt;/ul>
&lt;p>稍微总结下，sidecar具有两个明显优势，一个是多语言开发维护成本低 ，另一个是独立升级，当然代价是需要增加一点点的网络延迟。至此大家是不是觉得Sidecar基本完美？ 别着急，需要大家再思考一个问题：SDK模式下中间件组件会随应用一起发布，拥有完全一致的生命周期；而在sidecar模式下，如何管理sidecar的生命周期？这里可以拿无线耳机来举个例子，无线耳机是独立了，但必须独立电源的驱动，所以充电是要的。是的，在大规模的集群中这个点会带来不小的复杂性。&lt;/p>
&lt;h2 id="关键点">关键点&lt;/h2>
&lt;p>&lt;img alt="4.png | center | 826x206" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/dubbomesh/4.png">&lt;/p>
&lt;p>下面跟大家分享下我们对servicemesh理解的三个关键技术点。分别是sidecar运维，数据面与控制面的集成，协议。&lt;/p>
&lt;ul>
&lt;li>先说sidecar的运维，这是个难点，也是为什么sidecar方案以前没有被广泛应用的重要原因。前面说sidecar与应用现在成为两个不同的进程，要考虑多个事宜，一是要考虑如何把sidecar与应用部署在一起，二是考虑业务进程或sidecar进程一方需要升级重启时如何协同来保证请求的正常处理或转发，即优雅上下线的问题。这些事宜考虑清楚并解决后，算是具备servicemesh的前提条件。当然，kubernetes解决了这块的事情，提供了initiator类似插件的机制来对原子性的pod进行注入sidecar，并通过健康检查机制来保证两个进程的协同。简单地也可以这么理解：先把kubernetes容器调度平台的实施是servicemesh的前提条件。&lt;/li>
&lt;li>数据面中的sidecar的服务治理能力则是其核心竞争力，包括负载均衡策略，路由，安全，权重等等，这些能力是以规则形式通过控制面来统一下发给数据面。在传统微服务框架下数据面和控制面的集成是紧耦合，也就是数据面和控制面是一体的，举例来说用了Dubbo框架，只能选择Dubbo-Ops。而Envoy作为servicemesh思潮的带领者，提出了一整套的API规范，Istio可以实现其xDS接口，阿里巴巴也可以根据自己的架构设计实现类似的服务平台。&lt;/li>
&lt;li>协议 协议 协议， 重要的事说三遍。。。sidecar和Dubbo的内核是网络协议的处理器，而sidecar又是面向多语言场景的，所以自然协议处理能力是要强调的。先说下阿里Dubbo当下向Mesh方向发展时遇到难点。首先我们的服务接口都是通过Java Interface描述，其次涉及的传输模型DTO也是Java POJO定义，最后协议也是私有的。这会导致跨语言比较难，而sidecar形态需要面向多语言，这些问题更是首当其冲。考虑到这里有点稍微偏细节点，希望大家带着如下问题来先思考下：业务应用到sidecar之间的数据交换要考虑什么? Sidecar自身在处理网络字节流时又要考虑什么？是的，首先业务应用最好都不依赖特定协议库，也不依赖特接口定义库；Sidecar自身处理数据时跟nginx很接近，但最好具备协议转换适配的能力，比如把基于HTTP的请求转换为Dubbo请求，就能轻松集成Dubbo遗留系统。&lt;/li>
&lt;/ul>
&lt;h2 id="回看协议">回看协议&lt;/h2>
&lt;p>&lt;img alt="5.png | center | 826x206" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/dubbomesh/5.png">&lt;/p></description></item><item><title>Dubbo服务端异步接口的实现背景和实践</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/11/02/dubbo%E6%9C%8D%E5%8A%A1%E7%AB%AF%E5%BC%82%E6%AD%A5%E6%8E%A5%E5%8F%A3%E7%9A%84%E5%AE%9E%E7%8E%B0%E8%83%8C%E6%99%AF%E5%92%8C%E5%AE%9E%E8%B7%B5/</link><pubDate>Sat, 02 Nov 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/11/02/dubbo%E6%9C%8D%E5%8A%A1%E7%AB%AF%E5%BC%82%E6%AD%A5%E6%8E%A5%E5%8F%A3%E7%9A%84%E5%AE%9E%E7%8E%B0%E8%83%8C%E6%99%AF%E5%92%8C%E5%AE%9E%E8%B7%B5/</guid><description>&lt;h2 id="铺垫">铺垫&lt;/h2>
&lt;p>建议先对Dubbo的处理过程中涉及的线程阶段先做个了解，具体可参考&lt;a href="https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/11/01/dubbo%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%BC%82%E6%AD%A5%E6%8E%A5%E5%8F%A3%E7%9A%84%E5%AE%9E%E7%8E%B0%E8%83%8C%E6%99%AF%E5%92%8C%E5%AE%9E%E8%B7%B5/">Dubbo客户端异步接口的实现背景和使用场景&lt;/a>。&lt;/p>
&lt;h2 id="实现背景">实现背景&lt;/h2>
&lt;p>有必要比较详细点的介绍下服务端的线程策略来加深用户在选择服务端异步的判断依据，同时有必要引出协程这一在服务端异步中常常会用到的“秘密武器”。&lt;/p>
&lt;h3 id="服务端的线程策略">服务端的线程策略&lt;/h3>
&lt;p>Dubbo支持多种NIO框架来做Remoting的协议实现，无论是Netty，Mina或者Grizzly，实现都大同小异，都是基于事件驱动的方式来做网络通道建立，数据流读取的。其中以Grizzly对于&lt;a href="https://javaee.github.io/grizzly/iostrategies.html">线程策略&lt;/a>的介绍为例，通常支持以下四种。Dubbo作为一个RPC框架，默认选择的是第一种策略，原因在于业务服务是CPU密集型还是IO阻塞型，是无法断定的，第一种策略是最保险的策略。当然，对于这几种策略有了了解后，再结合业务场景做针对性的选择是最完美的。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Worker-thread策略&lt;/strong>&lt;/li>
&lt;/ol>
&lt;p>最常用最普适的策略，其中IO线程将NIO事件处理委托给工作线程。&lt;/p>
&lt;p>&lt;img alt="workerthread-strategy.png" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/dubboasyn_server/1.png">&lt;/p>
&lt;p>此策略具有很高的伸缩性。我们可以根据需要更改IO和worker线程池的大小，并且不存在在特定NIO事件处理期间可能发生的，同一Selector各个Channel之间相互干扰的风险。&lt;/p>
&lt;p>缺点是有线程上下文切换的代价。&lt;/p>
&lt;ol start="2">
&lt;li>&lt;strong>Same-thread策略&lt;/strong>&lt;/li>
&lt;/ol>
&lt;p>可能是最有效的策略。与第一种不同，同一线程处理当前线程中的NIO事件，避免了昂贵的线程上下文切换。&lt;/p>
&lt;p>&lt;img alt="samethread-strategy.png" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/dubboasyn_server/2.png">&lt;/p>
&lt;p>这个策略可以调整IO线程池大小，也是具备可伸缩性；缺点也很明显，它要求业务处理中一定不要有阻塞处理，因为它可能会阻止在同一个IO线程上发生的其他NIO事件的处理。&lt;/p>
&lt;ol start="3">
&lt;li>&lt;strong>Dynamic策略&lt;/strong>&lt;/li>
&lt;/ol>
&lt;p>如前所述，前两种策略具有明显的优点和缺点。但是，如果策略可以尝试在运行时根据当前条件（负载，收集的统计信息等）巧妙地交换它们，何如？&lt;/p>
&lt;p>&lt;img alt="dynamic-strategy.png" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/dubboasyn_server/3.png">&lt;/p>
&lt;p>这种策略可能会带来很多好处，能更好地控制资源，前提是不要使条件评估逻辑过载，防止评估判断的复杂性会使这种策略效率低下。
多说一句，希望大家对这个策略多留意一下，它可能是Dubbo服务端异步方式的最佳搭配。我也多扯个淡，这几天关注了些adaptive XX或者predictive XX，这里看到dynamic真是亲切，Dubbo作为产品级生产级的微服务解决方案，是必须既要adaptive，又要predictive，还要dynamic，哈哈。&lt;/p>
&lt;ol start="4">
&lt;li>&lt;strong>Leader-follower策略&lt;/strong>&lt;/li>
&lt;/ol>
&lt;p>&lt;img alt="leaderfollower-strategy.png" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/dubboasyn_server/4.png">&lt;/p>
&lt;p>此策略类似于第一种，但它不是将NIO事件处理传递给worker线程，而是通过将控制传递给Selector给工作线程，并将实际NIO事件处理当前IO线程中。这种策略其实是把worker和IO线程阶段做了混淆，个人不建议。&lt;/p>
&lt;h3 id="协程与线程">协程与线程&lt;/h3>
&lt;p>在CPU资源的管理上，OS和JVM的最小调度单位都是线程，业务应用通过扩展实现的协程包是可以具备独立的运行单位，事实上也是基于线程来做的，核心应该是遇到IO阻塞，或者锁等待时，保存上下文，然后切换到另一个协程。至于说的协程开销低，能更高效的使用CPU，这些考虑到协程库的用户态实现和上下文设计是支持的，但也建议大家结合实际业务场景做性能测试。&lt;/p>
&lt;p>&lt;strong>在默认的Dubbo线程策略中，是有worker线程池来执行业务逻辑，但也常常会发生ThreadPool Full的问题，为了尽快释放worker线程，在业务服务的实现中会另起线程。代价是再次增加线程上下文切换，同时需要考虑链路级别的数据传送(比如tracing信息)和流控的出口控制等等。当然，如果Dubbo能够切换到Same-thread策略，再配合协程库的支持，服务端异步是一种值得推荐的使用方式。&lt;/strong>&lt;/p>
&lt;h2 id="示例">示例&lt;/h2>
&lt;p>通过示例来体验下Dubbo服务端异步接口。Demo代码请访问github之&lt;a href="https://github.com/dubbo/dubbo-samples/tree/master/2-advanced/dubbo-samples-notify">https://github.com/dubbo/dubbo-samples/tree/master/2-advanced/dubbo-samples-notify&lt;/a>。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">AsyncServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> AsyncService {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">sayHello&lt;/span>(String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(&lt;span style="color:#2aa198">&amp;#34;Main sayHello() method start.&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">final&lt;/span> AsyncContext asyncContext &lt;span style="color:#719e07">=&lt;/span> RpcContext.startAsync();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> Thread(() &lt;span style="color:#719e07">-&amp;gt;&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> asyncContext.signalContextSwitch();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(&lt;span style="color:#2aa198">&amp;#34;Attachment from consumer: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> RpcContext.getContext().getAttachment(&lt;span style="color:#2aa198">&amp;#34;consumer-key1&amp;#34;&lt;/span>));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(&lt;span style="color:#2aa198">&amp;#34; -- Async start.&amp;#34;&lt;/span>);
&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> Thread.sleep(500);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">catch&lt;/span> (InterruptedException e) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> e.printStackTrace();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> asyncContext.write(&lt;span style="color:#2aa198">&amp;#34;Hello &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, response from provider.&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(&lt;span style="color:#2aa198">&amp;#34; -- Async end.&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }).start();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(&lt;span style="color:#2aa198">&amp;#34;Main sayHello() method end.&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;hello, &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="实践建议">实践建议&lt;/h2>
&lt;ul>
&lt;li>不要迷信服务端异步。&lt;/li>
&lt;li>服务端异步在Event-Driven或者Reactive面前基本是伪命题.&lt;span data-type="color" style="color:rgb(36, 41, 46)">&lt;span data-type="background" style="background-color:rgb(255, 255, 255)">补充下原因：服务端异步初衷是说Dubbo的服务端业务线程数（默认是200个）不够，但其实在event-driven模式下，200个肯定不需要那么多，只需要cpu核数那样就可以。只要业务实现是非阻塞的纯异步方式的业务逻辑处理，用再多的线程数都是浪费资源。&lt;/span>&lt;/span>&lt;/li>
&lt;li>要用服务端异步，建议服务端的线程策略采用same thread模式+协程包。&lt;/li>
&lt;/ul>
&lt;h2 id="小结">小结&lt;/h2>
&lt;p>Dubbo在支持业务应用时，会碰到千奇百怪的需求场景，服务端异步为用户提供了一种解决ThreadPool Full的方案。当发生ThreadPool Full的情况下，如果当前系统瓶颈是CPU，不建议用这种方案；如果系统Load不高，调高worker的线程数目，或者采用服务端异步，都是可以考虑的。&lt;/p></description></item><item><title>Dubbo客户端异步接口的实现背景和实践</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/11/01/dubbo%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%BC%82%E6%AD%A5%E6%8E%A5%E5%8F%A3%E7%9A%84%E5%AE%9E%E7%8E%B0%E8%83%8C%E6%99%AF%E5%92%8C%E5%AE%9E%E8%B7%B5/</link><pubDate>Fri, 01 Nov 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/11/01/dubbo%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%BC%82%E6%AD%A5%E6%8E%A5%E5%8F%A3%E7%9A%84%E5%AE%9E%E7%8E%B0%E8%83%8C%E6%99%AF%E5%92%8C%E5%AE%9E%E8%B7%B5/</guid><description>&lt;h2 id="铺垫">铺垫&lt;/h2>
&lt;p>&lt;img alt="image | left" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/dubboasyn_client/1.png">&lt;/p>
&lt;p>先简单介绍下一次完整的Dubbo调用所经历的线程阶段。几个信息这里罗列下&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Biz&lt;del>代表业务线程，即便是业务逻辑处理所处的线程，Biz&lt;/del>线程池可能是业务自己创建维护，大多数的可能是系统框架自身管理的（比如web型的业务系统跑在Tomcat容器下，Biz&lt;del>线程就是Tomcat维护）；IO&lt;/del>代表网络数据处理线程，是IO框架（比如Netty，Grizzly）创建维护，Dubbo Remoting所默认Netty实现是NioEventloopLoopGroup；另外按照Channel与IO线程的绑定关系，也可以直接把IO~看成一个可接受事件消息的Channel。像Biz和IO这样的异步处理阶段在JDK8中有个很精确地抽象描述，叫CompletionStage。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>大家知道，线程与线程之间做数据通信的方式是共享变量，Biz和IO两个stage之间的数据通信是Queue，具体到Dubbo实现，在客户端一侧的实现（即上图中用1所标注的步骤）中Biz是通过向EventLoop的LinkedBlockingQueue放置一个Task，而EventLoop有对应的Thread会不停的迭代Queue来执行Task中所包含的信息，具体代码可以看SingleThreadEventExecutor（顺便提下，Netty中默认是用无上限的LinkedBlockingQueue，在Biz的速率高于网络速率情况下，似乎好像有Memory Leak的风险）。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>如上图所示，标准的一次RPC调用经过了图中所示的1,2,3,4的四次消息(事件)传递，分别是客户端业务线程到IO线程的请求发出，服务端IO线程到业务逻辑线程的__请求接受，__服务端处理完成后由业务逻辑线程到IO线程的响应写出，客户端收到结果后从IO线程到业务逻辑的响应处理。除了1与4之间一般需要维护响应和请求的映射对应关系，四次的事件处理都是完全独立的，所以一次RPC调用天然是异步的，而同步是基于异步而来。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="客户端异步">客户端异步&lt;/h2>
&lt;h3 id="实现背景">实现背景&lt;/h3>
&lt;p>在Java语言（其他语言不清楚）下一次本地接口的调用可以透明地通过代理机制转为远程RPC的调用，大多数业务方也比较喜欢这种与本地接口类似的编程方式做远程服务集成，所以虽然RPC内部天然是异步的，但使用Dubbo的用户使用最广泛的还是同步，而异步反而成为小众的使用场景。同步的优点是编程模型更加符合业务方的“传统”习惯，代价是在图中的1代表的请求发出事件后需要阻塞当前的Biz&lt;del>线程，一直等到4代表的响应处理后才能唤醒。在这个短则微秒级别，长则秒级的1,2,3,4处理过程中都要阻塞Biz&lt;/del>线程，就会消耗线程资源，增加系统资源的开销。&lt;/p>
&lt;p>所以，客户端异步的出发点是节省线程资源开销，代价是需要了解下异步的使用方式:)。在同步方式下API接口的返回类型是代表着某个业务类，而当异步情况下，响应返回与请求发出是完全独立的两个事件，需要API接口的返回类型变为上述中说的CompletionStage才是最贴合的，这是Dubbo在异步上支持的必然异步。回到最近的Dubbo发布版，是不改变接口的情况下，需要在服务创建时注册一个回调接口来处理响应返回事件。&lt;/p>
&lt;p>下面以示例来说。&lt;/p>
&lt;h3 id="示例">示例&lt;/h3>
&lt;p>事件通知的示例代码请参考：&lt;a href="https://github.com/dubbo/dubbo-samples/tree/master/2-advanced/dubbo-samples-notify">https://github.com/dubbo/dubbo-samples/tree/master/dubbo-samples-notify&lt;/a>&lt;/p>
&lt;p>事件通知允许 Consumer 端在调用之前、调用正常返回之后或调用出现异常时，触发 &lt;code>oninvoke&lt;/code>、&lt;code>onreturn&lt;/code>、&lt;code>onthrow&lt;/code> 三个事件。&lt;/p>
&lt;p>可以通过在配置 Consumer 时，指定事件需要通知的方法，如：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;bean&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;demoCallback&amp;#34;&lt;/span> class=&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.samples.notify.impl.NotifyImpl&amp;#34;&lt;/span> &lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span> check=&lt;span style="color:#2aa198">&amp;#34;false&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.samples.notify.api.DemoService&amp;#34;&lt;/span> version=&lt;span style="color:#2aa198">&amp;#34;1.0.0&amp;#34;&lt;/span> group=&lt;span style="color:#2aa198">&amp;#34;cn&amp;#34;&lt;/span>&lt;span style="color:#268bd2">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo:method&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;sayHello&amp;#34;&lt;/span> onreturn=&lt;span style="color:#2aa198">&amp;#34;demoCallback.onreturn&amp;#34;&lt;/span> onthrow=&lt;span style="color:#2aa198">&amp;#34;demoCallback.onthrow&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/dubbo:reference&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>其中，NotifyImpl 的代码如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">NotifyImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> Notify{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>Integer, String&lt;span style="color:#719e07">&amp;gt;&lt;/span> ret &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> HashMap&lt;span style="color:#719e07">&amp;lt;&lt;/span>Integer, String&lt;span style="color:#719e07">&amp;gt;&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">onreturn&lt;/span>(String name, &lt;span style="color:#dc322f">int&lt;/span> id) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ret.put(id, name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(&lt;span style="color:#2aa198">&amp;#34;onreturn: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">onthrow&lt;/span>(Throwable ex, String name, &lt;span style="color:#dc322f">int&lt;/span> id) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(&lt;span style="color:#2aa198">&amp;#34;onthrow: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里要强调一点，自定义 Notify 接口中的三个方法的参数规则如下：&lt;/p></description></item><item><title>Dubbo 在跨语言和协议穿透性方向上的探索：支持 HTTP/2 gRPC 和 Protobuf</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/10/28/dubbo-%E5%9C%A8%E8%B7%A8%E8%AF%AD%E8%A8%80%E5%92%8C%E5%8D%8F%E8%AE%AE%E7%A9%BF%E9%80%8F%E6%80%A7%E6%96%B9%E5%90%91%E4%B8%8A%E7%9A%84%E6%8E%A2%E7%B4%A2%E6%94%AF%E6%8C%81-http/2-grpc-%E5%92%8C-protobuf/</link><pubDate>Mon, 28 Oct 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/10/28/dubbo-%E5%9C%A8%E8%B7%A8%E8%AF%AD%E8%A8%80%E5%92%8C%E5%8D%8F%E8%AE%AE%E7%A9%BF%E9%80%8F%E6%80%A7%E6%96%B9%E5%90%91%E4%B8%8A%E7%9A%84%E6%8E%A2%E7%B4%A2%E6%94%AF%E6%8C%81-http/2-grpc-%E5%92%8C-protobuf/</guid><description>&lt;p>本文总体上可分为基础产品简介、Dubbo 对 gRPC (HTTP/2) 和 Protobuf 的支持及示例演示三部分，在简介部分介绍了 Dubbo、HTTP/2、gRPC、Protobuf 的基本概念和特点；第二部分介绍了 Dubbo 为何要支持 gRPC (HTTP/2) 和 Protobuf，以及这种支持为 gRPC 和 Dubbo 开发带来的好处与不同；第三部分通过两个实例分别演示了 Dubbo gRPC 和 Dubbo Protobuf 的使用方式。&lt;/p>
&lt;h2 id="基本介绍">基本介绍&lt;/h2>
&lt;h3 id="dubbo-协议">Dubbo 协议&lt;/h3>
&lt;p>从协议层面展开，以下是当前 2.7 版本支持的 Dubbo 协议&lt;/p>
&lt;p>&lt;img alt="image-20191029103919557" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/grpc/dubbo-ptotocol.png">&lt;/p>
&lt;p>众所周知，Dubbo 协议是直接定义在 TCP 传输层协议之上，由于 TCP 高可靠全双工的特点，为 Dubbo 协议的定义提供了最大的灵活性，但同时也正是因为这样的灵活性，RPC 协议普遍都是定制化的私有协议，Dubbo 同样也面临这个问题。在这里我们着重讲一下 Dubbo 在协议通用性方面值得改进的地方，关于协议详细解析请参见&lt;a href="https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/10/05/dubbo-%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3/">官网博客&lt;/a>&lt;/p>
&lt;ul>
&lt;li>Dubbo 协议体 Body 中有一个可扩展的 attachments 部分，这给 RPC 方法之外额外传递附加属性提供了可能，是一个很好的设计。但是类似的 Header 部分，却缺少类似的可扩展 attachments，这点可参考 HTTP 定义的 Ascii Header 设计，将 Body Attachments 和 Header Attachments 做职责划分。&lt;/li>
&lt;li>Body 协议体中的一些 RPC 请求定位符如 Service Name、Method Name、Version 等，可以提到 Header 中，和具体的序列化协议解耦，以更好的被网络基础设施识别或用于流量管控。&lt;/li>
&lt;li>扩展性不够好，欠缺协议升级方面的设计，如 Header 头中没有预留的状态标识位，或者像 HTTP 有专为协议升级或协商设计的特殊 packet。&lt;/li>
&lt;li>在 Java 版本的代码实现上，不够精简和通用。如在链路传输中，存在一些语言绑定的内容；消息体中存在冗余内容，如 Service Name 在 Body 和 Attachments 中都存在。&lt;/li>
&lt;/ul>
&lt;h3 id="http1">HTTP/1&lt;/h3>
&lt;p>相比于直接构建与 TPC 传输层的私有 RPC 协议，构建于 HTTP 之上的远程调用解决方案会有更好的通用性，如WebServices 或 REST 架构，使用 HTTP + JSON 可以说是一个事实标准的解决方案。&lt;/p></description></item><item><title>本地存根和本地伪装</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/10/22/%E6%9C%AC%E5%9C%B0%E5%AD%98%E6%A0%B9%E5%92%8C%E6%9C%AC%E5%9C%B0%E4%BC%AA%E8%A3%85/</link><pubDate>Tue, 22 Oct 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/10/22/%E6%9C%AC%E5%9C%B0%E5%AD%98%E6%A0%B9%E5%92%8C%E6%9C%AC%E5%9C%B0%E4%BC%AA%E8%A3%85/</guid><description>&lt;h2 id="基本概念">基本概念&lt;/h2>
&lt;p>典型的 RPC 调用客户端是依赖并且只依赖接口编程来进行远程调用的。在真正发起远程调用之前，用户往往需要做一些预处理的工作，比如提前校验参数。在拿到返回调用结果之后，用户可能需要缓存结果，或者是在调用失败的时候构造容错数据，而不是简单的抛出异常。&lt;/p>
&lt;p>这个时候，用户可以编写出类似以下的代码来处理上面提出的这些场景：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">try&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> preProcess();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> service.invoke(...);
&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">return&lt;/span> mockValue;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>} &lt;span style="color:#719e07">finally&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> postProcess();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>类似的，用户也可以通过面向切面编程 &lt;em>AOP&lt;/em> 的高级技巧来解决上面的诉求，比如通过 &lt;em>Spring AOP&lt;/em> 的方式可以通过类似下面的这段配置来完成。使用 &lt;em>AOP&lt;/em> 的技巧相比上面的代码来说，避免了容错处理等与业务无关的代码对业务代码的侵入，使得业务处理主逻辑更简洁。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;bean&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;demo-service-stub&amp;#34;&lt;/span> class=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.demo.DemoServiceStub&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;bean&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;demo-service-mock&amp;#34;&lt;/span> class=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.demo.DemoServiceMock&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;aop:config&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;aop:aspect&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;stub&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;demo-service-stub&amp;#34;&lt;/span>&lt;span style="color:#268bd2">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;aop:pointcut&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;stubPointcut&amp;#34;&lt;/span> expression=&lt;span style="color:#2aa198">&amp;#34;execution(* org.apache.dubbo.samples.DemoService+.*(..))&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;aop:before&lt;/span> method=&lt;span style="color:#2aa198">&amp;#34;preProcess&amp;#34;&lt;/span> pointcut-ref=&lt;span style="color:#2aa198">&amp;#34;stubPointcut&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;aop:after-returning&lt;/span> method=&lt;span style="color:#2aa198">&amp;#34;postProcess&amp;#34;&lt;/span> pointcut-ref=&lt;span style="color:#2aa198">&amp;#34;stubPointcut&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/aop:aspect&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;aop:aspect&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;mock&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;demo-service-mock&amp;#34;&lt;/span>&lt;span style="color:#268bd2">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;aop:pointcut&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;mockPointcut&amp;#34;&lt;/span> expression=&lt;span style="color:#2aa198">&amp;#34;execution(* org.apache.dubbo.samples.DemoService+.*(..))&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;aop:after-throwing&lt;/span> method=&lt;span style="color:#2aa198">&amp;#34;mock&amp;#34;&lt;/span> pointcut-ref=&lt;span style="color:#2aa198">&amp;#34;mockPointcut&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/aop:aspect&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/aop:config&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>为了进一步的方便用户做 Dubbo 开发，框架提出了本地存根 &lt;em>Stub&lt;/em> 和本地伪装 &lt;em>Mock&lt;/em> 的概念。通过约定大于配置的理念，进一步的简化了配置，使用起来更加方便，并且不依赖额外的 &lt;em>AOP&lt;/em> 框架就达到了 &lt;em>AOP&lt;/em> 的效果。&lt;/p></description></item><item><title>研究 Dubbo 网卡地址注册时的一点思考</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/10/01/%E7%A0%94%E7%A9%B6-dubbo-%E7%BD%91%E5%8D%A1%E5%9C%B0%E5%9D%80%E6%B3%A8%E5%86%8C%E6%97%B6%E7%9A%84%E4%B8%80%E7%82%B9%E6%80%9D%E8%80%83/</link><pubDate>Tue, 01 Oct 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/10/01/%E7%A0%94%E7%A9%B6-dubbo-%E7%BD%91%E5%8D%A1%E5%9C%B0%E5%9D%80%E6%B3%A8%E5%86%8C%E6%97%B6%E7%9A%84%E4%B8%80%E7%82%B9%E6%80%9D%E8%80%83/</guid><description>&lt;h2 id="1-如何选择合适的网卡地址">1 如何选择合适的网卡地址&lt;/h2>
&lt;p>可能相当一部分人还不知道我这篇文章到底要讲什么，我说个场景，大家应该就明晰了。在分布式服务调用过程中，以 Dubbo 为例，服务提供者往往需要将自身的 IP 地址上报给注册中心，供消费者去发现。在大多数情况下 Dubbo 都可以正常工作，但如果你留意过 Dubbo 的 github issue，其实有不少人反馈：Dubbo Provider 注册了错误的 IP。如果你能立刻联想到：多网卡、内外网地址共存、VPN、虚拟网卡等关键词，那我建议你一定要继续将本文看下去，因为我也想到了这些，它们都是本文所要探讨的东西！那么“如何选择合适的网卡地址”呢，Dubbo 现有的逻辑到底算不算完备？我们不急着回答它，而是带着这些问题一起进行研究，相信到文末，其中答案，各位看官自有评说。&lt;/p>
&lt;h2 id="2-dubbo-是怎么做的">2 Dubbo 是怎么做的&lt;/h2>
&lt;p>Dubbo 获取网卡地址的逻辑在各个版本中也是千回百转，走过弯路，也做过优化，我们用最新的 2.7.2-SNAPSHOT 版本来介绍，在看以下源码时，大家可以怀着质疑的心态去阅读，在 dubbo github 的 master 分支可以获取源码。获取 localhost 的逻辑位于 &lt;code>org.apache.dubbo.common.utils.NetUtils#getLocalAddress0()&lt;/code> 之中&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> InetAddress &lt;span style="color:#268bd2">getLocalAddress0&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> InetAddress localAddress &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 首先尝试获取 /etc/hosts 中 hostname 对应的 IP&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> localAddress &lt;span style="color:#719e07">=&lt;/span> InetAddress.getLocalHost();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Optional&lt;span style="color:#719e07">&amp;lt;&lt;/span>InetAddress&lt;span style="color:#719e07">&amp;gt;&lt;/span> addressOp &lt;span style="color:#719e07">=&lt;/span> toValidAddress(localAddress);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (addressOp.isPresent()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> addressOp.get();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 没有找到适合注册的 IP，则开始轮询网卡&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Enumeration&lt;span style="color:#719e07">&amp;lt;&lt;/span>NetworkInterface&lt;span style="color:#719e07">&amp;gt;&lt;/span> interfaces &lt;span style="color:#719e07">=&lt;/span> NetworkInterface.getNetworkInterfaces();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (&lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">==&lt;/span> interfaces) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> localAddress;
&lt;/span>&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">while&lt;/span> (interfaces.hasMoreElements()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> NetworkInterface network &lt;span style="color:#719e07">=&lt;/span> interfaces.nextElement();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Enumeration&lt;span style="color:#719e07">&amp;lt;&lt;/span>InetAddress&lt;span style="color:#719e07">&amp;gt;&lt;/span> addresses &lt;span style="color:#719e07">=&lt;/span> network.getInetAddresses();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">while&lt;/span> (addresses.hasMoreElements()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 返回第一个匹配的适合注册的 IP&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Optional&lt;span style="color:#719e07">&amp;lt;&lt;/span>InetAddress&lt;span style="color:#719e07">&amp;gt;&lt;/span> addressOp &lt;span style="color:#719e07">=&lt;/span> toValidAddress(addresses.nextElement());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (addressOp.isPresent()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> addressOp.get();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> localAddress;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Dubbo 这段选取本地地址的逻辑大致分成了两步&lt;/p></description></item><item><title>Dubbo Admin服务测试功能</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/08/26/service-test/</link><pubDate>Mon, 26 Aug 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/08/26/service-test/</guid><description>&lt;p>基于Dubbo2.7的元数据，Dubbo Admin实现了服务测试功能，可以通过泛化调用，在控制台上调用真实的服务提供者&lt;/p>
&lt;h2 id="使用方式">使用方式&lt;/h2>
&lt;ul>
&lt;li>部署服务提供者： 可以在&lt;a href="https://github.com/nzomkxia/dubbo-demo">这里&lt;/a>下载demo，此工程基于spring boot，方便在IDE或者命令行启动，对于服务测试来说，只需要启动&lt;code>dubbo-basic-provider&lt;/code>即可。&lt;/li>
&lt;li>服务查询： 完成服务端部署后，可以到Dubbo Admin的&lt;code>服务测试&lt;/code>页面上查询对应的服务:
&lt;img alt="testSearch" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/admin/testSearch.jpg">&lt;br>
这里的信息和元数据类似，包含方法名，参数类型和返回值信息，点击右边的标签就可以进入服务测试页面&lt;/li>
&lt;li>服务测试：
&lt;img alt="testSuccess" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/admin/testSuccess.jpg">&lt;br>
服务测试页面包含了两个json编辑器，参数类型的信息都是以json格式保存，这里需要填入对应的参数值(本例中数类型时&lt;code>String&lt;/code>)，填写完成后点击&lt;code>执行&lt;/code>即可对服务端发起调用，调用结果展示在右边的编辑器中，如果调用失败，会显示详细的失败原因，下面来看一下调用失败的例子：&lt;br>
&lt;img alt="testFail" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/admin/testFail.jpg">
本例中，先关掉Dubbo服务提供者的进程，再执行服务测试，可以看到返回的结果是&lt;code>找不到服务提供者&lt;/code>的异常。和普通调用一样，业务和框架的异常都会返回在结果中，方便业务排查。&lt;/li>
&lt;li>复合类型参数&lt;br>
考虑&lt;code>UserService&lt;/code>中的以下方法和类型：&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//org.apache.dubbo.demo.api.UserService&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Result &lt;span style="color:#268bd2">getUser&lt;/span>(String name, UserInfoDO userInfoDO);
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">UserInfoDO&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> id;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> LocationDO locationDO;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> DepartmentDO departmentDO;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">toString&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;UserInfoDO{&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;id=&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> id &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;, locationDO=&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> locationDO.toString() &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;, departmentDO=&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> departmentDO.toString() &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#39;}&amp;#39;&lt;/span>;
&lt;/span>&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;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DepartmentDO&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> String departName;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> LocationDO departLocation;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">toString&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;DepartmentDO{&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;departName=&amp;#39;&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> departName &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#39;\&amp;#39;&amp;#39;&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;, departLocation=&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> departLocation.toString() &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#39;}&amp;#39;&lt;/span>;
&lt;/span>&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;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">LocationDO&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> String address;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> postNum;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">toString&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;LocationDO{&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;address=&amp;#39;&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> address &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#39;\&amp;#39;&amp;#39;&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;, postNum=&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> postNum &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#39;}&amp;#39;&lt;/span>;
&lt;/span>&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>参数是比较复杂的符合类型参数，服务测试的时候，会逐层展开填写每一个field的值，如下图所示：&lt;br>
&lt;img alt="complex" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/admin/complex.jpg">
同样可以调用成功并且返回结果&lt;/p></description></item><item><title>本地调用</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/08/11/%E6%9C%AC%E5%9C%B0%E8%B0%83%E7%94%A8/</link><pubDate>Sun, 11 Aug 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/08/11/%E6%9C%AC%E5%9C%B0%E8%B0%83%E7%94%A8/</guid><description>&lt;h3 id="本地调用介绍">本地调用介绍&lt;/h3>
&lt;p>当一个应用既是一个服务的提供者，同时也是这个服务的消费者的时候，可以直接对本机提供的服务发起本地调用。从 &lt;code>2.2.0&lt;/code> 版本开始，Dubbo 默认在本地以 &lt;em>injvm&lt;/em> 的方式暴露服务，这样的话，在同一个进程里对这个服务的调用会优先走本地调用。&lt;/p>
&lt;p>与本地对象上方法调用不同的是，Dubbo 本地调用会经过 Filter 链，其中包括了 Consumer 端的 Filter 链以及 Provider 端的 Filter 链。通过这样的机制，本地消费者和其他消费者都是统一对待，统一监控，服务统一进行治理。&lt;/p>
&lt;p>&lt;img alt="filter-chain" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/dubbo-local-call-filter.png">&lt;/p>
&lt;p>同时，相比于远程调用来说，Dubbo 本地调用性能较优，省去了请求、响应的编解码及网络传输的过程。&lt;/p>
&lt;p>要使用 Dubbo 本地调用不需做特殊配置，按正常 Dubbo 服务暴露服务即可。任一服务在暴露远程服务的同时，也会同时以 &lt;em>injvm&lt;/em> 的协议暴露本地服务。&lt;em>injvm&lt;/em> 是一个伪协议，不会像其他协议那样对外开启端口，只用于本地调用的目的。&lt;/p>
&lt;p>以下面的 XML 配置为例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:registry&lt;/span> address=&lt;span style="color:#2aa198">&amp;#34;zookeeper://127.0.0.1:2181&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:protocol&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span> port=&lt;span style="color:#2aa198">&amp;#34;20800&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;bean&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;demoServiceTarget&amp;#34;&lt;/span> class=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.local.impl.DemoServiceImpl&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:service&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.local.api.DemoService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;demoServiceTarget&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.local.api.DemoService&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里同时配置了同一服务 &lt;em>DemoService&lt;/em> 的提供者以及消费者。在这种情况下，该应用中的 &lt;em>DemoService&lt;/em> 的消费方会优先使用 &lt;em>injvm&lt;/em> 协议进行本地调用。上述的例子可以在 dubbo-samples 工程中找到源码：https://github.com/apache/dubbo-samples/blob/master/dubbo-samples-local&lt;/p>
&lt;h3 id="细粒度控制本地调用">细粒度控制本地调用&lt;/h3>
&lt;p>本地调用是可以显示关闭的，通过这种方式，服务提供者可以做到对远端服务消费者和本地消费者一视同仁。具体做法是通过 &lt;em>scope=&amp;ldquo;remote&amp;rdquo;&lt;/em> 来关闭 &lt;em>injvm&lt;/em> 协议的暴露，这样，即使是本地调用者，也需要从注册中心上获取服务地址列表，然后才能发起调用，而这个时候的调用过程，与远端的服务消费者的过程是一致的。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;bean&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;target&amp;#34;&lt;/span> class=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.local.impl.DemoServiceImpl&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&amp;lt;!-- 服务提供者指定 scope=&amp;#34;remote&amp;#34; --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:service&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.local.api.DemoService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;target&amp;#34;&lt;/span> scope=&lt;span style="color:#2aa198">&amp;#34;remote&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.local.api.DemoService&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>同样的，服务消费者也支持通过 &lt;em>scope&lt;/em> 来限定发起调用优先走本地，还是只走远程。比如，可以通过以下的方式强制消费端通过&lt;strong>远程调用&lt;/strong>的方式来发起 dubbo 调用：&lt;/p></description></item><item><title>在 Dubbo 中使用 REST</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/07/26/%E5%9C%A8-dubbo-%E4%B8%AD%E4%BD%BF%E7%94%A8-rest/</link><pubDate>Fri, 26 Jul 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/07/26/%E5%9C%A8-dubbo-%E4%B8%AD%E4%BD%BF%E7%94%A8-rest/</guid><description>&lt;h2 id="什么是-rest">什么是 REST&lt;/h2>
&lt;p>REST 是 Roy Thomas Fielding &lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> 在 2000 年他的博士论文 &lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup> “架构风格以及基于网络的软件架构设计” 中提出来的一个概念。REST 是 &lt;strong>RE&lt;/strong>presentational &lt;strong>S&lt;/strong>tate &lt;strong>T&lt;/strong>ransfer 的缩写，翻译过来就是 “表现层状态转化”。REST 就是 Roy 在这篇论文中提出的面向互联网的软件所应当具备的架构风格。&lt;/p>
&lt;p>按照 REpresentational State Transfer 的字面意思，可以把应用看成是一个虚拟的状态机，软件提供的不是服务而是一系列的&lt;strong>资源&lt;/strong>，对这些资源的访问通过&lt;strong>统一的操作&lt;/strong>来访问，而返回的结果代表了资源状态的一次跃迁。REST 是一种架构风格，如果一个软件架构符合 REST 风格，就可以称之为 RESTful 架构。这个架构应当具备以下一些设计上的约束：资源具有唯一标示、资源之间有关联关系、使用标准的方式来访问、资源有多种表现形式、无状态交互。&lt;/p>
&lt;p>举例来说，一个简单的静态 HTML 页面的网站就很好的符合了 RESTful 架构风格。访问 &lt;code>http://example.com/accounts&lt;/code> 返回一个包含所有账号的页面，选取其中一个链接 &lt;code>http://example.com/accounts/1&lt;/code> 又会返回包含用户 1 的详细信息。爬虫软件在这种场景下工作的很好，当知道了某个网站的首页地址后，可以自举发现这个网站上所有关联的网页。更重要的是，这种访问形式不依赖网站提供的任何客户端，而是仅仅通过 HTTP 标准的访问方式完成的。可以说，HTML 这种超媒体文档的组织形式就是资源的表现层状态迁移的一种形式。&lt;/p>
&lt;p>对于一个提供服务的动态网站来说，可以按照类似的思路将其 RESTful 化：&lt;/p>
&lt;ul>
&lt;li>
&lt;p>GET &lt;code>http://example.com/accounts&lt;/code> 返回所有账号信息&lt;/p>
&lt;/li>
&lt;li>
&lt;p>POST &lt;code>http://example.com/accounts&lt;/code> 创建一个新的账号&lt;/p>
&lt;/li>
&lt;li>
&lt;p>GET &lt;code>http://example.com/accounts/1&lt;/code> 返回账号 1 的信息&lt;/p>
&lt;/li>
&lt;li>
&lt;p>DELETE &lt;code>http://example.com/accounts/1&lt;/code> 删除账号 1&lt;/p>
&lt;/li>
&lt;li>
&lt;p>PUT &lt;code>http://example.com/accounts/1&lt;/code> 更新账号 1 信息&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>其中的思路是利用 HTTP 协议的标准方法 POST、DELETE、PUT、GET 来表达对于一个资源的增删改查 (CRUD) 操作，利用 URL 来表示一个资源的唯一标识。对资源访问的错误码也复用 HTTP 协议的状态码。返回结果通常由 json 或 XML 来表示，如果其中包括了对关联资源的访问方式 (所谓的表现层状态迁移) ，这种类型的 RESTful 应用可以进一步的称之为 &lt;em>hypermedia as the engine of application state&lt;/em> (HATEOAS) 应用 &lt;sup id="fnref:3">&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref">3&lt;/a>&lt;/sup>。&lt;/p></description></item><item><title>使用 Dubbo 连接异构微服务体系</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/06/22/%E4%BD%BF%E7%94%A8-dubbo-%E8%BF%9E%E6%8E%A5%E5%BC%82%E6%9E%84%E5%BE%AE%E6%9C%8D%E5%8A%A1%E4%BD%93%E7%B3%BB/</link><pubDate>Sat, 22 Jun 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/06/22/%E4%BD%BF%E7%94%A8-dubbo-%E8%BF%9E%E6%8E%A5%E5%BC%82%E6%9E%84%E5%BE%AE%E6%9C%8D%E5%8A%A1%E4%BD%93%E7%B3%BB/</guid><description>&lt;p>从编程开发的角度来说，Dubbo 首先是一款 RPC 服务框架，它最大的优势在于提供了面向接口代理的服务编程模型，对开发者屏蔽了底层的远程通信细节。同时 Dubbo 也是一款服务治理框架，它为分布式部署的微服务提供了服务发现、流量调度等服务治理解决方案。&lt;/p>
&lt;p>在这篇文章中，我们将以以上基础能力为背景，尝试突破 Dubbo 体系自身，探索如何利用 Dubbo 对多协议、多服务发现模型的支持，来实现异构微服务体系间的互联互通。在实际业务场景中，这可以用来解决异构技术体系共存场景下的通信问题，帮助公司实现在异构技术体系间作平滑迁移，解决大规模跨区域、多集群部署场景的地址发现及流量调度等问题。&lt;/p>
&lt;h2 id="面向接口代理的透明服务开发框架">面向接口代理的透明服务开发框架&lt;/h2>
&lt;p>我们还是从 &lt;strong>Dubbo 是一个微服务开发框架&lt;/strong> 这个大家熟知的概念开始。就像 Spring 是开发 Java 应用的基础框架一样，我们经常会选用 Dubbo 作为开发微服务业的基础框架。 Dubbo 框架的最大优势我认为就在其面向接口的编程模型，使得开发远程服务调用就像开发本地服务一样（以 Java 语言为例）：&lt;/p>
&lt;ol>
&lt;li>服务定义&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">GreetingsService&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHi&lt;/span>(String name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>消费方调用服务&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 和调用本地服务一样，完全透明。&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Reference&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> GreetingService greetingService;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">doSayHello&lt;/span>(String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> greetingService.sayHi(&lt;span style="color:#2aa198">&amp;#34;Hello world!&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>下图是 Dubbo 的基本工作原理图，服务提供者与服务消费者之间通过注册中心协调地址，通过约定的协议实现数据交换。&lt;/p>
&lt;p>&lt;img alt="Dubbo basic work flow" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/architecture.png">&lt;/p>
&lt;h2 id="同构异构微服务体系面临的问题">同构/异构微服务体系面临的问题&lt;/h2>
&lt;p>关于 Dubbo 协议本身及其服务治理相关功能细节并不是本文的重点，我们今天将从一个更高的层次，来看看公司内部构建微服务体系所面的挑战，以及 Dubbo 能为架构选型和迁移等提供哪些解决思路。&lt;/p>
&lt;p>一个公司内部的微服务可能都是基于某一个相同的服务框架开发的，比如说 Dubbo，对于这样的架构，我们称之为是&lt;strong>同构的微服务体系&lt;/strong>；而有些公司的微服务可能是使用多个不同的服务框架所建设，我们称之为&lt;strong>异构的微服务体系&lt;/strong>，多个不同技术栈微服务体系的共存在大型组织内还是非常普遍的，造成这种局面可能有很多原因。比如，可能是遗留系统带来的，也可能是公司正在做技术栈迁移，或者就是不同业务部门为了满足各自特殊需求而做的独立选型（这也意味着异构微服务体系的长期共存）。&lt;/p>
&lt;p>&lt;strong>1. 异构微服务体系共存&lt;/strong>&lt;/p>
&lt;p>我们很容易想到的一个挑战是：&lt;strong>不同的体系间通常是使用不同的 RPC 通信协议、部署独立的注册中心集群，面对这种多协议、多注册中心集群的场景，要如何实现相互之间透明的地址发现和透明的 RPC 调用？&lt;/strong> 如果我们什么都不做，那么每个微服务体系就只能感知到自己体系内的服务状态，流量也在各自的体系内封闭。而要做到从体系 A 平滑的迁移到体系 B，或者想长期的保持公司内部多个体系的共存，则解决不同体系间的互联互通，实现流量的透明调度将是非常重要的环节。&lt;/p></description></item><item><title>Dubbo可扩展机制源码解析</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/05/02/dubbo%E5%8F%AF%E6%89%A9%E5%B1%95%E6%9C%BA%E5%88%B6%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/</link><pubDate>Thu, 02 May 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/05/02/dubbo%E5%8F%AF%E6%89%A9%E5%B1%95%E6%9C%BA%E5%88%B6%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/</guid><description>&lt;p>在&lt;a href="https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/04/25/dubbo%E5%8F%AF%E6%89%A9%E5%B1%95%E6%9C%BA%E5%88%B6%E5%AE%9E%E6%88%98/">Dubbo可扩展机制实战&lt;/a>中，我们了解了Dubbo扩展机制的一些概念，初探了Dubbo中LoadBalance的实现，并自己实现了一个LoadBalance。是不是觉得Dubbo的扩展机制很不错呀，接下来，我们就深入Dubbo的源码，一睹庐山真面目。&lt;/p>
&lt;h2 id="extensionloader">ExtensionLoader&lt;/h2>
&lt;p>ExtensionLoader 是最核心的类，负责扩展点的加载和生命周期管理。我们就以这个类开始吧。
ExtensionLoader 的方法比较多，比较常用的方法有:&lt;/p>
&lt;ul>
&lt;li>&lt;code>public static &amp;lt;T&amp;gt; ExtensionLoader&amp;lt;T&amp;gt; getExtensionLoader(Class&amp;lt;T&amp;gt; type)&lt;/code>&lt;/li>
&lt;li>&lt;code>public T getExtension(String name)&lt;/code>&lt;/li>
&lt;li>&lt;code>public T getAdaptiveExtension()&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>比较常见的用法有:&lt;/p>
&lt;ul>
&lt;li>&lt;code>LoadBalance lb = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(loadbalanceName)&lt;/code>&lt;/li>
&lt;li>&lt;code>RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getAdaptiveExtension()&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>说明：在接下来展示的源码中，我会将无关的代码(比如日志，异常捕获等)去掉，方便大家阅读和理解。&lt;/p>
&lt;ol>
&lt;li>getExtensionLoader方法
这是一个静态工厂方法，入参是一个可扩展的接口，返回一个该接口的ExtensionLoader实体类。通过这个实体类，可以根据name获得具体的扩展，也可以获得一个自适应扩展。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">getExtensionLoader&lt;/span>(Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> type) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 扩展点必须是接口&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (&lt;span style="color:#719e07">!&lt;/span>type.isInterface()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException(&lt;span style="color:#2aa198">&amp;#34;Extension type(&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> type &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;) is not interface!&amp;#34;&lt;/span>);
&lt;/span>&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">// 必须要有@SPI注解&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (&lt;span style="color:#719e07">!&lt;/span>withExtensionAnnotation(type)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException(&lt;span style="color:#2aa198">&amp;#34;Extension type without @SPI Annotation!&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:#586e75">// 从缓存中根据接口获取对应的ExtensionLoader&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 每个扩展只会被加载一次&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> loader &lt;span style="color:#719e07">=&lt;/span> (ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span>) EXTENSION_LOADERS.get(type);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (loader &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 初始化扩展&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> EXTENSION_LOADERS.putIfAbsent(type, &lt;span style="color:#719e07">new&lt;/span> ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span>(type));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> loader &lt;span style="color:#719e07">=&lt;/span> (ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span>) EXTENSION_LOADERS.get(type);
&lt;/span>&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> loader;
&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">ExtensionLoader&lt;/span>(Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> type) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>.type &lt;span style="color:#719e07">=&lt;/span> type;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> objectFactory &lt;span style="color:#719e07">=&lt;/span> (type &lt;span style="color:#719e07">==&lt;/span> ExtensionFactory.class &lt;span style="color:#719e07">?&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>getExtension方法&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> T &lt;span style="color:#268bd2">getExtension&lt;/span>(String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Holder&lt;span style="color:#719e07">&amp;lt;&lt;/span>Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> holder &lt;span style="color:#719e07">=&lt;/span> cachedInstances.get(name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (holder &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> cachedInstances.putIfAbsent(name, &lt;span style="color:#719e07">new&lt;/span> Holder&lt;span style="color:#719e07">&amp;lt;&lt;/span>Object&lt;span style="color:#719e07">&amp;gt;&lt;/span>());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> holder &lt;span style="color:#719e07">=&lt;/span> cachedInstances.get(name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Object instance &lt;span style="color:#719e07">=&lt;/span> holder.get();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 从缓存中获取，如果不存在就创建&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (instance &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">synchronized&lt;/span> (holder) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> instance &lt;span style="color:#719e07">=&lt;/span> holder.get();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (instance &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> instance &lt;span style="color:#719e07">=&lt;/span> createExtension(name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> holder.set(instance);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> (T) instance;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>getExtension 方法中做了一些判断和缓存，主要的逻辑在 createExtension 方法中。我们继续看 createExtension 方法。&lt;/p></description></item><item><title>Dubbo 一致性Hash负载均衡实现剖析</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/05/01/dubbo-%E4%B8%80%E8%87%B4%E6%80%A7hash%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1%E5%AE%9E%E7%8E%B0%E5%89%96%E6%9E%90/</link><pubDate>Wed, 01 May 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/05/01/dubbo-%E4%B8%80%E8%87%B4%E6%80%A7hash%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1%E5%AE%9E%E7%8E%B0%E5%89%96%E6%9E%90/</guid><description>&lt;p>需要强调的是，Dubbo的Hash映射模型与大部分网上资料描述的&lt;strong>环形队列Hash映射模型&lt;/strong>是存在一些区别的。于我而言，环形队列Hash映射模型，不足以让我对一致性Hash有足够彻底的了解。直到看懂了Dubbo的一致性Hash的实现，才觉得豁然开朗。&lt;/p>
&lt;h3 id="一环形队列hash映射模型">一、环形队列Hash映射模型&lt;/h3>
&lt;p>这种方案，其基础还是基于取模运算。对2^32取模，那么，Hash值的区间为[0, 2^32-1]。接下来要做的，就包括两部分：&lt;/p>
&lt;h4 id="a映射服务">&lt;strong>a、映射服务&lt;/strong>&lt;/h4>
&lt;p>将服务地址（ip+端口）按照一定规则构造出特定的识别码（如md5码），再用识别码对2^32取模，确定服务在Hash值区间对应的位置。假设有Node1、Node2、Node3三个服务，其映射关系如下：&lt;/p>
&lt;p>&lt;img alt="Init" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/consistenthash/consistent-hash-init-model.jpg">&lt;/p>
&lt;h4 id="b映射请求定位服务">&lt;strong>b、映射请求、定位服务&lt;/strong>&lt;/h4>
&lt;p>在发起请求时，我们往往会带上参数，而这些参数，就可以被我们用来确定具体调用哪一个服务。假设有请求R1、R2、R3，对它们的参数也经过计算特定识别码、取余的一系列运算之后，有如下映射关系：&lt;/p>
&lt;p>&lt;img alt="Request" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/consistenthash/consistent-hash-request-model.jpg">&lt;/p>
&lt;p>从图中，我们可以看到，R1请求映射在0-Node1中间，R2请求映射在Node1-Node2中间，R3请求映射在Node2-Node3中间。我们取&lt;strong>服务Hash值大于等于请求Hash值&lt;/strong>的&lt;strong>第一个服务&lt;/strong>作为实际的调用服务。也就是说，R1请求将调用Node1服务，R2请求将调用Node2服务，R3请求将调用Node3服务。&lt;/p>
&lt;h4 id="c新增服务节点">&lt;strong>c、新增服务节点&lt;/strong>&lt;/h4>
&lt;p>假设新增服务Node4，映射在Node3之前，恰巧破坏了原来的一个映射关系：&lt;/p>
&lt;p>&lt;img alt="New Node" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/consistenthash/consistent-hash-new-node-model.jpg">&lt;/p>
&lt;p>这样，请求R3将会实际调用服务Node4，但请求R1、R2不受影响。&lt;/p>
&lt;h4 id="d删除服务节点">&lt;strong>d、删除服务节点&lt;/strong>&lt;/h4>
&lt;p>假设服务Node2宕机，那么R2请求将会映射到Node3：&lt;/p>
&lt;p>&lt;img alt="Delete Node" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/consistenthash/consistent-hash-delete-node-model.jpg">&lt;/p>
&lt;p>原本的R1、R3请求不受影响。&lt;/p>
&lt;blockquote>
&lt;p>可以看出，当新增、删除服务时，受影响的请求是有限的。不至于像简单取模映射一般，服务发生变化时，需要调整全局的映射关系。&lt;/p>
&lt;/blockquote>
&lt;h4 id="e平衡性与虚拟节点">&lt;strong>e、平衡性与虚拟节点&lt;/strong>&lt;/h4>
&lt;p>在我们上面的假设中，我们假设Node1、Node2、Node3三个服务在经过Hash映射后所分布的位置恰巧把环切成了均等的三分，请求的分布也基本是平衡的。但是实际上计算服务Hash值的时候，是很难这么巧的。也许一不小心就映射成了这个样子：&lt;/p>
&lt;p>&lt;img alt="Balance" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/consistenthash/consistent-hash-balance-model.jpg">&lt;/p>
&lt;p>这样，就会导致大部分请求都会被映射到Node1上。因此，引出了虚拟节点。&lt;/p>
&lt;p>所谓虚拟节点，就是除了对服务本身地址进行Hash映射外，还通过在它地址上做些处理（比如Dubbo中，在ip+port的字符串后加上计数符1、2、3&amp;hellip;&amp;hellip;，分别代表虚拟节点1、2、3），以达到同一服务映射多个节点的目的。通过引入虚拟节点，我们可以把上图中映射给Node1的请求进一步拆分：&lt;/p>
&lt;p>&lt;img alt="Virtual Node" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/consistenthash/consistent-hash-virtual-node-model.jpg">&lt;/p>
&lt;p>如上图所示，若有请求落在Node3-Node1&amp;rsquo;区间，该请求应该是调用Node1&amp;rsquo;服务，但是因为Node1&amp;rsquo;是Node1的虚拟节点，所以实际调用的是Node1服务。通过引入虚拟节点，请求的分布就会比较平衡了。&lt;/p>
&lt;h3 id="二dubbo一致性hash的使用与负载均衡策略的引入阶段">&lt;strong>二、Dubbo一致性Hash的使用与负载均衡策略的引入阶段&lt;/strong>&lt;/h3>
&lt;h4 id="a如何使用一致性hash作为dubbo的负载均衡策略">&lt;strong>a、如何使用一致性Hash作为Dubbo的负载均衡策略？&lt;/strong>&lt;/h4>
&lt;p>dubbo:service、dubbo:reference、dubbo:provider、dubbo:consumer、dubbo:method这几个配置项都可以配置Dubbo的负载均衡策略，其中一致性Hash的属性值是：&lt;strong>consistenthash&lt;/strong>。&lt;/p>
&lt;p>以dubbo:reference为例：&lt;/p>
&lt;p>&lt;strong>XML配置：&lt;/strong>&lt;/p>
&lt;blockquote>
&lt;p>&amp;lt;dubbo:reference loadbalance=&amp;ldquo;consistenthash&amp;rdquo; /&amp;gt;&lt;/p>
&lt;/blockquote>
&lt;p>&lt;strong>Properties配置：&lt;/strong>&lt;/p>
&lt;blockquote>
&lt;p>dubbo.reference.loadbalance=consistenthash&lt;/p>
&lt;/blockquote>
&lt;p>&lt;strong>注解：&lt;/strong>&lt;/p>
&lt;blockquote>
&lt;p>@Reference(loadbalance = &amp;ldquo;consistenthash&amp;rdquo;)&lt;/p>
&lt;/blockquote>
&lt;h4 id="bdubbo负载均衡策略的引入阶段">&lt;strong>b、Dubbo负载均衡策略的引入阶段&lt;/strong>&lt;/h4>
&lt;p>Dubbo实现的是客户端负载均衡。关于服务接口代理类的实现，这里不做详细描述，可以参考官网：&lt;/p>
&lt;blockquote>
&lt;p>服务引入：/zh-cn/docs/source_code_guide/refer-service.html&lt;/p>
&lt;/blockquote>
&lt;p>在接口代理类生成、并且装配好后，服务的调用基本是这样一个流程：proxy -&amp;gt; MockClusterInvoker -&amp;gt; 集群策略（如：FailoverClusterInvoker） -&amp;gt; 初始化负载均衡策略 -&amp;gt; 根据选定的负载均衡策略确定Invoker。&lt;/p>
&lt;p>&lt;strong>负载均衡策略的初始化&lt;/strong>是在AbstractClusterInvoker中的initLoadBalance方法中初始化的：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">protected&lt;/span> LoadBalance &lt;span style="color:#268bd2">initLoadBalance&lt;/span>(List&lt;span style="color:#719e07">&amp;lt;&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> invokers, Invocation invocation) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (CollectionUtils.isNotEmpty(invokers)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .getMethodParameter(RpcUtils.getMethodName(invocation), LOADBALANCE_KEY, DEFAULT_LOADBALANCE));
&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">return&lt;/span> ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(DEFAULT_LOADBALANCE);
&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;/p></description></item><item><title>Dubbo可扩展机制实战</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/04/25/dubbo%E5%8F%AF%E6%89%A9%E5%B1%95%E6%9C%BA%E5%88%B6%E5%AE%9E%E6%88%98/</link><pubDate>Thu, 25 Apr 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/04/25/dubbo%E5%8F%AF%E6%89%A9%E5%B1%95%E6%9C%BA%E5%88%B6%E5%AE%9E%E6%88%98/</guid><description>&lt;h2 id="1-dubbo的扩展机制">1. Dubbo的扩展机制&lt;/h2>
&lt;p>在Dubbo的官网上，Dubbo描述自己是一个高性能的RPC框架。今天我想聊聊Dubbo的另一个很棒的特性, 就是它的可扩展性。
如同罗马不是一天建成的，任何系统都一定是从小系统不断发展成为大系统的，想要从一开始就把系统设计的足够完善是不可能的，相反的，我们应该关注当下的需求，然后再不断地对系统进行迭代。在代码层面，要求我们适当的对关注点进行抽象和隔离，在软件不断添加功能和特性时，依然能保持良好的结构和可维护性，同时允许第三方开发者对其功能进行扩展。在某些时候，软件设计者对扩展性的追求甚至超过了性能。&lt;/p>
&lt;p>在谈到软件设计时，可扩展性一直被谈起，那到底什么才是可扩展性，什么样的框架才算有良好的可扩展性呢？它必须要做到以下两点:&lt;/p>
&lt;ol>
&lt;li>作为框架的维护者，在添加一个新功能时，只需要添加一些新代码，而不用大量的修改现有的代码，即符合开闭原则。&lt;/li>
&lt;li>作为框架的使用者，在添加一个新功能时，不需要去修改框架的源码，在自己的工程中添加代码即可。&lt;/li>
&lt;/ol>
&lt;p>Dubbo很好的做到了上面两点。这要得益于Dubbo的微内核+插件的机制。接下来的章节中我们会慢慢揭开Dubbo扩展机制的神秘面纱。&lt;/p>
&lt;h2 id="2-可扩展的几种解决方案">2. 可扩展的几种解决方案&lt;/h2>
&lt;p>通常可扩展的实现有下面几种:&lt;/p>
&lt;ul>
&lt;li>Factory模式&lt;/li>
&lt;li>IoC容器&lt;/li>
&lt;li>OSGI容器&lt;/li>
&lt;/ul>
&lt;p>Dubbo作为一个框架，不希望强依赖其他的IoC容器，比如Spring，Guice。OSGI也是一个很重的实现，不适合Dubbo。最终Dubbo的实现参考了Java原生的SPI机制，但对其进行了一些扩展，以满足Dubbo的需求。&lt;/p>
&lt;h2 id="3-java-spi机制">3. Java SPI机制&lt;/h2>
&lt;p>既然Dubbo的扩展机制是基于Java原生的SPI机制，那么我们就先来了解下Java SPI吧。了解了Java的SPI，也就是对Dubbo的扩展机制有一个基本的了解。如果对Java SPI比较了解的同学，可以跳过。&lt;/p>
&lt;p>Java SPI(Service Provider Interface)是JDK内置的一种动态加载扩展点的实现。在ClassPath的&lt;code>META-INF/services&lt;/code>目录下放置一个与接口同名的文本文件，文件的内容为接口的实现类，多个实现类用换行符分隔。JDK中使用&lt;code>java.util.ServiceLoader&lt;/code>来加载具体的实现。
让我们通过一个简单的例子，来看看Java SPI是如何工作的。&lt;/p>
&lt;ol>
&lt;li>定义一个接口IRepository用于实现数据储存&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">IRepository&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">save&lt;/span>(String data);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>提供IRepository的实现
IRepository有两个实现。MysqlRepository和MongoRepository。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">MysqlRepository&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> IRepository {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">save&lt;/span>(String data) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(&lt;span style="color:#2aa198">&amp;#34;Save &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> data &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; to Mysql&amp;#34;&lt;/span>);
&lt;/span>&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;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">MongoRepository&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> IRepository {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">save&lt;/span>(String data) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(&lt;span style="color:#2aa198">&amp;#34;Save &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> data &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; to Mongo&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="3">
&lt;li>添加配置文件
在&lt;code>META-INF/services&lt;/code>目录添加一个文件，文件名和接口全名称相同，所以文件是&lt;code>META-INF/services/com.demo.IRepository&lt;/code>。文件内容为:&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>com.demo.MongoRepository
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>com.demo.MysqlRepository
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="4">
&lt;li>通过ServiceLoader加载IRepository实现&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>ServiceLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>IRepository&lt;span style="color:#719e07">&amp;gt;&lt;/span> serviceLoader &lt;span style="color:#719e07">=&lt;/span> ServiceLoader.load(IRepository.class);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Iterator&lt;span style="color:#719e07">&amp;lt;&lt;/span>IRepository&lt;span style="color:#719e07">&amp;gt;&lt;/span> it &lt;span style="color:#719e07">=&lt;/span> serviceLoader.iterator();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">while&lt;/span> (it &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> it.hasNext()){
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> IRepository demoService &lt;span style="color:#719e07">=&lt;/span> it.next();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(&lt;span style="color:#2aa198">&amp;#34;class:&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> demoService.getClass().getName());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> demoService.save(&lt;span style="color:#2aa198">&amp;#34;tom&amp;#34;&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>在上面的例子中，我们定义了一个扩展点和它的两个实现。在ClassPath中添加了扩展的配置文件，最后使用ServiceLoader来加载所有的扩展点。
最终的输出结果为：
class:testDubbo.MongoRepository
Save tom to Mongo
class:testDubbo.MysqlRepository
Save tom to Mysql&lt;/p></description></item><item><title>提前if判断帮助CPU分支预测</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/02/03/%E6%8F%90%E5%89%8Dif%E5%88%A4%E6%96%AD%E5%B8%AE%E5%8A%A9cpu%E5%88%86%E6%94%AF%E9%A2%84%E6%B5%8B/</link><pubDate>Sun, 03 Feb 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/02/03/%E6%8F%90%E5%89%8Dif%E5%88%A4%E6%96%AD%E5%B8%AE%E5%8A%A9cpu%E5%88%86%E6%94%AF%E9%A2%84%E6%B5%8B/</guid><description>&lt;h2 id="分支预测">分支预测&lt;/h2>
&lt;p>在stackoverflow上有一个非常有名的问题：&lt;a href="https://stackoverflow.com/questions/11227809/why-is-it-faster-to-process-a-sorted-array-than-an-unsorted-array">为什么处理有序数组要比非有序数组快？&lt;/a>，可见分支预测对代码运行效率有非常大的影响。&lt;/p>
&lt;p>现代CPU都支持分支预测(branch prediction)和指令流水线(instruction pipeline)，这两个结合可以极大提高CPU效率。对于像简单的if跳转，CPU是可以比较好地做分支预测的。但是对于switch跳转，CPU则没有太多的办法。switch本质上是据索引，从地址数组里取地址再跳转。&lt;/p>
&lt;p>要提高代码执行效率，一个重要的原则就是尽量避免CPU把流水线清空，那么提高分支预测的成功率就非常重要。&lt;/p>
&lt;p>那么对于代码里，如果某个switch分支概率很高，是否可以考虑代码层面帮CPU把判断提前，来提高代码执行效率呢？&lt;/p>
&lt;h2 id="dubbo里channeleventrunnable的switch判断">Dubbo里ChannelEventRunnable的switch判断&lt;/h2>
&lt;p>在&lt;code>ChannelEventRunnable&lt;/code>里有一个switch来判断channel state，然后做对应的逻辑：&lt;a href="https://github.com/hengyunabc/dubbo/blob/dubbo-2.6.1/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispatcher/ChannelEventRunnable.java#L54">查看&lt;/a>&lt;/p>
&lt;p>一个channel建立起来之后，超过99.9%情况它的state都是&lt;code>ChannelState.RECEIVED&lt;/code>，那么可以考虑把这个判断提前。&lt;/p>
&lt;h2 id="benchmark验证">benchmark验证&lt;/h2>
&lt;p>下面通过jmh来验证下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">TestBenchMarks&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">enum&lt;/span> ChannelState {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		CONNECTED, DISCONNECTED, SENT, RECEIVED, CAUGHT
&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">@State&lt;/span>(Scope.Benchmark)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ExecutionPlan&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#268bd2">@Param&lt;/span>({ &lt;span style="color:#2aa198">&amp;#34;1000000&amp;#34;&lt;/span> })
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> size;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#268bd2">public&lt;/span> ChannelState&lt;span style="color:#719e07">[]&lt;/span> states &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#268bd2">@Setup&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">setUp&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			ChannelState&lt;span style="color:#719e07">[]&lt;/span> values &lt;span style="color:#719e07">=&lt;/span> ChannelState.values();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			states &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ChannelState&lt;span style="color:#719e07">[&lt;/span>size&lt;span style="color:#719e07">]&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			Random random &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Random(&lt;span style="color:#719e07">new&lt;/span> Date().getTime());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			&lt;span style="color:#719e07">for&lt;/span> (&lt;span style="color:#dc322f">int&lt;/span> i &lt;span style="color:#719e07">=&lt;/span> 0; i &lt;span style="color:#719e07">&amp;lt;&lt;/span> size; i&lt;span style="color:#719e07">++&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#dc322f">int&lt;/span> nextInt &lt;span style="color:#719e07">=&lt;/span> random.nextInt(1000000);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#719e07">if&lt;/span> (nextInt &lt;span style="color:#719e07">&amp;gt;&lt;/span> 100) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					states&lt;span style="color:#719e07">[&lt;/span>i&lt;span style="color:#719e07">]&lt;/span> &lt;span style="color:#719e07">=&lt;/span> ChannelState.RECEIVED;
&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>					states&lt;span style="color:#719e07">[&lt;/span>i&lt;span style="color:#719e07">]&lt;/span> &lt;span style="color:#719e07">=&lt;/span> values&lt;span style="color:#719e07">[&lt;/span>nextInt &lt;span style="color:#719e07">%&lt;/span> values.length&lt;span style="color:#719e07">]&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#268bd2">@Fork&lt;/span>(value &lt;span style="color:#719e07">=&lt;/span> 5)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#268bd2">@Benchmark&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#268bd2">@BenchmarkMode&lt;/span>(Mode.Throughput)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">benchSiwtch&lt;/span>(ExecutionPlan plan, Blackhole bh) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#dc322f">int&lt;/span> result &lt;span style="color:#719e07">=&lt;/span> 0;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#719e07">for&lt;/span> (&lt;span style="color:#dc322f">int&lt;/span> i &lt;span style="color:#719e07">=&lt;/span> 0; i &lt;span style="color:#719e07">&amp;lt;&lt;/span> plan.size; &lt;span style="color:#719e07">++&lt;/span>i) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			&lt;span style="color:#719e07">switch&lt;/span> (plan.states&lt;span style="color:#719e07">[&lt;/span>i&lt;span style="color:#719e07">]&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			&lt;span style="color:#719e07">case&lt;/span> CONNECTED:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				result &lt;span style="color:#719e07">+=&lt;/span> ChannelState.CONNECTED.ordinal();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#719e07">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			&lt;span style="color:#719e07">case&lt;/span> DISCONNECTED:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				result &lt;span style="color:#719e07">+=&lt;/span> ChannelState.DISCONNECTED.ordinal();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#719e07">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			&lt;span style="color:#719e07">case&lt;/span> SENT:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				result &lt;span style="color:#719e07">+=&lt;/span> ChannelState.SENT.ordinal();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#719e07">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			&lt;span style="color:#719e07">case&lt;/span> RECEIVED:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				result &lt;span style="color:#719e07">+=&lt;/span> ChannelState.RECEIVED.ordinal();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#719e07">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			&lt;span style="color:#719e07">case&lt;/span> CAUGHT:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				result &lt;span style="color:#719e07">+=&lt;/span> ChannelState.CAUGHT.ordinal();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#719e07">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		bh.consume(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;span style="display:flex;">&lt;span>	&lt;span style="color:#268bd2">@Fork&lt;/span>(value &lt;span style="color:#719e07">=&lt;/span> 5)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#268bd2">@Benchmark&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#268bd2">@BenchmarkMode&lt;/span>(Mode.Throughput)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">benchIfAndSwitch&lt;/span>(ExecutionPlan plan, Blackhole bh) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#dc322f">int&lt;/span> result &lt;span style="color:#719e07">=&lt;/span> 0;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#719e07">for&lt;/span> (&lt;span style="color:#dc322f">int&lt;/span> i &lt;span style="color:#719e07">=&lt;/span> 0; i &lt;span style="color:#719e07">&amp;lt;&lt;/span> plan.size; &lt;span style="color:#719e07">++&lt;/span>i) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			ChannelState state &lt;span style="color:#719e07">=&lt;/span> plan.states&lt;span style="color:#719e07">[&lt;/span>i&lt;span style="color:#719e07">]&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			&lt;span style="color:#719e07">if&lt;/span> (state &lt;span style="color:#719e07">==&lt;/span> ChannelState.RECEIVED) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				result &lt;span style="color:#719e07">+=&lt;/span> ChannelState.RECEIVED.ordinal();
&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">switch&lt;/span> (state) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#719e07">case&lt;/span> CONNECTED:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					result &lt;span style="color:#719e07">+=&lt;/span> ChannelState.CONNECTED.ordinal();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					&lt;span style="color:#719e07">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#719e07">case&lt;/span> SENT:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					result &lt;span style="color:#719e07">+=&lt;/span> ChannelState.SENT.ordinal();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					&lt;span style="color:#719e07">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#719e07">case&lt;/span> DISCONNECTED:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					result &lt;span style="color:#719e07">+=&lt;/span> ChannelState.DISCONNECTED.ordinal();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					&lt;span style="color:#719e07">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#719e07">case&lt;/span> CAUGHT:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					result &lt;span style="color:#719e07">+=&lt;/span> ChannelState.CAUGHT.ordinal();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					&lt;span style="color:#719e07">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		bh.consume(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;ul>
&lt;li>benchSiwtch里是纯switch判断&lt;/li>
&lt;li>benchIfAndSwitch 里用一个if提前判断state是否&lt;code>ChannelState.RECEIVED&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>benchmark结果是：&lt;/p></description></item><item><title>浅谈 RPC</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/01/07/%E6%B5%85%E8%B0%88-rpc/</link><pubDate>Mon, 07 Jan 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2019/01/07/%E6%B5%85%E8%B0%88-rpc/</guid><description>&lt;p>近几年随着微服务化项目的崛起，逐渐成为许多公司中大型分布式系统架构的主流方式，而今天所说的 RPC 在这其中扮演着至关重要的角色。随着这段日子公司项目微服务化的演进，发现在日常开发中都在隐式或显式的使用 RPC，一些刚刚接触 RPC 的小伙伴会感觉无所适从，而一些入行多年的老手虽然使用 RPC 经验丰富，但有些对其原理也一知半解，缺乏对原理的深入理解，往往也会造成开发中的一些误用。&lt;/p>
&lt;h2 id="什么是rpc">什么是RPC？&lt;/h2>
&lt;p>RPC（Remote Procedure Call）—远程过程调用，它是一种通过网络从远程计算机程序上请求服务，而不需要了解底层网络技术的协议。也就是说两台服务器A，B，一个应用部署在A服务器上，想要调用B服务器上应用提供的方法，由于不在一个内存空间，不能直接调用，需要通过网络来表达调用的语义和传达调用的数据。&lt;/p>
&lt;p>RPC协议假定某些传输协议的存在，如TCP或UDP，为通信程序之间携带信息数据。在OSI网络通信模型中，RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。现在业界有很多开源的优秀 RPC 框架，例如 Spring Cloud、Dubbo、Thrift 等。&lt;/p>
&lt;h2 id="rpc-起源">RPC 起源&lt;/h2>
&lt;p>RPC 这个概念术语在上世纪 80 年代由 &lt;strong>Bruce Jay Nelson&lt;/strong> 提出。这里我们追溯下当初开发 RPC 的原动机是什么？在 Nelson 的论文 &amp;ldquo;Implementing Remote Procedure Calls&amp;rdquo; 中他提到了几点：&lt;/p>
&lt;ul>
&lt;li>简单：RPC 概念的语义十分清晰和简单，这样建立分布式计算就更容易。&lt;/li>
&lt;li>高效：过程调用看起来十分简单而且高效。&lt;/li>
&lt;li>通用：在单机计算中过程往往是不同算法部分间最重要的通信机制。&lt;/li>
&lt;/ul>
&lt;p>通俗一点说，就是一般程序员对于本地的过程调用很熟悉，那么我们把 RPC 作成和本地调用完全类似，那么就更容易被接受，使用起来毫无障碍。Nelson 的论文发表于 30 年前，其观点今天看来确实高瞻远瞩，今天我们使用的 RPC 框架基本就是按这个目标来实现的。&lt;/p>
&lt;h2 id="rpc-结构">RPC 结构&lt;/h2>
&lt;p>Nelson 的论文中指出实现 RPC 的程序包括 5 个部分：&lt;/p>
&lt;ol>
&lt;li>User&lt;/li>
&lt;li>User-stub&lt;/li>
&lt;li>RPCRuntime&lt;/li>
&lt;li>Server-stub&lt;/li>
&lt;li>Server&lt;/li>
&lt;/ol>
&lt;p>&lt;img alt="RPC结构" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/rpc/rpc-structure-1.png">&lt;/p>
&lt;p>这里 user 就是 client 端，当 user 想发起一个远程调用时，它实际是通过本地调用 user-stub。user-stub 负责将调用的接口、方法和参数通过约定的协议规范进行编码并通过本地的 RPCRuntime 实例传输到远端的实例。远端 RPCRuntime 实例收到请求后交给 server-stub 进行解码后发起本地端调用，调用结果再返回给 user 端。&lt;/p></description></item><item><title>Dubbo服务分组和版本聚合</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/10/27/dubbo%E6%9C%8D%E5%8A%A1%E5%88%86%E7%BB%84%E5%92%8C%E7%89%88%E6%9C%AC%E8%81%9A%E5%90%88/</link><pubDate>Sat, 27 Oct 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/10/27/dubbo%E6%9C%8D%E5%8A%A1%E5%88%86%E7%BB%84%E5%92%8C%E7%89%88%E6%9C%AC%E8%81%9A%E5%90%88/</guid><description>&lt;p>我们在调用Dubbo服务的时候，一般只需要将Consumer端的&lt;code>dubbo:reference&lt;/code>指定成服务端中&lt;code>dubbo:service&lt;/code>暴露的服务，就可以找到服务端，完成调用，也就是说，Dubbo只需要服务接口信息就可以找到服务提供者。
其实除了服务提供者以外，Dubbo也有服务分组和版本的概念，在客户端去寻找“匹配”的服务端的时候，需要服务接口，版本号，组别这三个信息都匹配，才算是一个有效的服务端：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> &lt;span style="color:#268bd2">isMatch&lt;/span>(URL consumerUrl, URL providerUrl) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String consumerInterface &lt;span style="color:#719e07">=&lt;/span> consumerUrl.getServiceInterface();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String providerInterface &lt;span style="color:#719e07">=&lt;/span> providerUrl.getServiceInterface();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (&lt;span style="color:#719e07">!&lt;/span>(Constants.ANY_VALUE.equals(consumerInterface) &lt;span style="color:#719e07">||&lt;/span> StringUtils.isEquals(consumerInterface, providerInterface)))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (&lt;span style="color:#719e07">!&lt;/span>isMatchCategory(providerUrl.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> consumerUrl.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY))) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (&lt;span style="color:#719e07">!&lt;/span>providerUrl.getParameter(Constants.ENABLED_KEY, &lt;span style="color:#cb4b16">true&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">!&lt;/span>Constants.ANY_VALUE.equals(consumerUrl.getParameter(Constants.ENABLED_KEY))) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String consumerGroup &lt;span style="color:#719e07">=&lt;/span> consumerUrl.getParameter(Constants.GROUP_KEY);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String consumerVersion &lt;span style="color:#719e07">=&lt;/span> consumerUrl.getParameter(Constants.VERSION_KEY);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String consumerClassifier &lt;span style="color:#719e07">=&lt;/span> consumerUrl.getParameter(Constants.CLASSIFIER_KEY, Constants.ANY_VALUE);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String providerGroup &lt;span style="color:#719e07">=&lt;/span> providerUrl.getParameter(Constants.GROUP_KEY);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String providerVersion &lt;span style="color:#719e07">=&lt;/span> providerUrl.getParameter(Constants.VERSION_KEY);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String providerClassifier &lt;span style="color:#719e07">=&lt;/span> providerUrl.getParameter(Constants.CLASSIFIER_KEY, Constants.ANY_VALUE);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> (Constants.ANY_VALUE.equals(consumerGroup) &lt;span style="color:#719e07">||&lt;/span> StringUtils.isEquals(consumerGroup, providerGroup) &lt;span style="color:#719e07">||&lt;/span> StringUtils.isContains(consumerGroup, providerGroup))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> (Constants.ANY_VALUE.equals(consumerVersion) &lt;span style="color:#719e07">||&lt;/span> StringUtils.isEquals(consumerVersion, providerVersion))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> (consumerClassifier &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">||&lt;/span> Constants.ANY_VALUE.equals(consumerClassifier) &lt;span style="color:#719e07">||&lt;/span> StringUtils.isEquals(consumerClassifier, providerClassifier));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果没有配置组别和版本号，默认值为空。服务端和消费端都没有配，只有服务接口，其他两个信息都为空，也是可以“找到”对方的，那服务名和版本号可以如何使用呢？下面我们来看一下具体的场景：&lt;/p></description></item><item><title>Dubbo 协议详解</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/10/05/dubbo-%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3/</link><pubDate>Fri, 05 Oct 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/10/05/dubbo-%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3/</guid><description>&lt;h2 id="协议的概念">协议的概念&lt;/h2>
&lt;p>协议是两个网络实体进行通信的基础，数据在网络上从一个实体传输到另一个实体，以字节流的形式传递到对端。在这个字节流的世界里，如果没有协议，就无法将这个一维的字节流重塑成为二维或者多维的数据结构以及领域对象。&lt;/p>
&lt;h3 id="协议是什么">协议是什么&lt;/h3>
&lt;p>协议是双方确定的交流语义，比如：我们设计一个字符串传输的协议，它允许客户端发送一个字符串，服务端接收到对应的字符串。这个协议很简单，首先发送一个4字节的消息总长度，然后再发送1字节的字符集charset长度，接下来就是消息的payload，字符集名称和字符串正文。&lt;/p>
&lt;p>发送一个&lt;code>iso-8859-1&lt;/code>的字符串&lt;code>abc&lt;/code>到对端。经过协议编码，内容是：&lt;code>18 = 4 + 1 + 10 + 3|10|iso-8859-1|abc&lt;/code>，当这些字节流发往服务端后，当服务端收到字节流后，首先读取4个字节，将其转换为int，在这个例子中是18，接下来继续读14个字节，将首个字节得到字符集名称长度10，将后续内容的前10字节转换为字符串，内容是&lt;code>iso-8859-1&lt;/code>，使用该字符集将后续的字节数组造型成为字符串&lt;code>new String(bytes, &amp;quot;iso-8859-1&amp;quot;)&lt;/code>。&lt;/p>
&lt;p>在前面自定义字符串传输协议的例子中，我们已经看到协议在双方传输数据中起到的作用，没有协议就无法完成数据交换，下面是维基百科对于通信协议的定义。&lt;/p>
&lt;blockquote>
&lt;p>In telecommunication, a communication protocol is a system of rules that allow two or more entities of a communications system to transmit information via any kind of variation of a physical quantity. The protocol defines the rules syntax, semantics and synchronization of communication and possible error recovery methods. Protocols may be implemented by hardware, software, or a combination of both.&lt;/p></description></item><item><title>Dubbo与Kubernetes集成</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/09/30/dubbo%E4%B8%8Ekubernetes%E9%9B%86%E6%88%90/</link><pubDate>Sun, 30 Sep 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/09/30/dubbo%E4%B8%8Ekubernetes%E9%9B%86%E6%88%90/</guid><description>&lt;h2 id="大体目标">大体目标&lt;/h2>
&lt;p>Dubbo的provider不再关心服务注册的事宜，只需要把其Dubbo服务端口打开，由Kubernetes来进行服务的声明和发布；Dubbo的consumer在服务发现时直接发现kubernetes的对应服务endpoints，从而复用Dubbo已有的微服务通道能力。好处是无需依赖三方的软负载注册中心；同时无缝融入Kubernetes的多租户安全体系。Demo的代码参照： &lt;a href="https://github.com/dubbo/dubbo-kubernetes">https://github.com/dubbo/dubbo-kubernetes&lt;/a>&lt;/p>
&lt;h2 id="闲谈">闲谈&lt;/h2>
&lt;p>Kubernates是建立在扩展性的具备二次开发的功能层次丰富的体系化系统&lt;/p>
&lt;ul>
&lt;li>首先其最核心的功能是管理容器集群，能管理容器化的集群（包括存储，计算），当然这个是建立在对容器运行时(CRI)，网络接口(CNI),存储服务接口（CSI/FV）的基础上；&lt;/li>
&lt;li>其次是面向应用(包括无状态/有状态,批处理/服务型应用)的部署和路由能力，特别是基于微服务架构的应用管理，具备了其服务定义和服务发现，以及基于configmap的统一配置能力；&lt;/li>
&lt;li>在基础资源（主要是抽象底层IaaS的资源）和应用层的抽象模型之上是治理层，包含弹性扩容，命名空间/租户，等。当然，基于其原子内核的基础能力，在Kubernetes的核心之上搭建统一的日志中心和全方位监控等服务是水到渠成的，CNCF更是有其认定推荐。&lt;/li>
&lt;/ul>
&lt;p>来张Kubernetes Architecture的一张图解释下上述描述。在2018年Kubernetes往事实的paas底座的标配迈出质的一步，有人说原因在于基于扩展的二次开发能力，有人说在于其声明式编程和背靠Google和Redhat的强大社区运作，我觉得回归本质是在于下图中的&lt;strong>Layered架构和其问题域的领域建模抽象&lt;/strong>。&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/k8s/1.png">&lt;/p>
&lt;p>以微服务架构视角，Kubernetes在一定意义上是微服务框架（这时较叫微服务平台或toolkit集更合适），支持微服务的服务发现/注册的基本能力。借用如下图做一个简单描述。&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/k8s/2.jpeg">&lt;/p>
&lt;p>话题再展开一下，微服务领域涉及众多问题，大概可以用下图说明。&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/k8s/3.jpeg">&lt;/p>
&lt;p>Kubernetes解决得只是少部分，而像动态路由，稳定性控制（断路器，隔水舱等），分布式服务追踪等是个空白，这也就是servicemesh要解决的，是在CNCF的Trail Map占有重要一席；当然Dubbo是基本具备完备的微服务，也就是使得其集成到k8s体系下具有相当的意义。Dubbo在serviemesh中基于sidecar的方案是解决跨语言诉求的通用servicemesh方案，需要新开一个话题来展开说；而引用serviemsh的原始定义：&lt;/p>
&lt;blockquote>
&lt;p>A service mesh is a dedicated infrastructure layer for handling service-to-service communication. It’s responsible for the reliable delivery of requests through the complex topology of services that comprise a modern, cloud native application.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>首先服务网格是一个云原生环境下基础设施层，功能在于处理服务间通信，职责是负责实现请求的可靠传递，被使得被监控跟踪，被治理，最终使得微服务架构被赋予高可控的稳定性和快速的问题定位排查能力。&lt;/p>
&lt;/blockquote>
&lt;p>可以得出现有Dubbo集成云原生基础设施Kubernetes的基础能力而并解决微服务相关核心问题也算是一种狭义上的servicemesh方案，只是是Java领域的罢了；当玩笑理解也行，哈哈。&lt;/p>
&lt;h2 id="思路方案">思路/方案&lt;/h2>
&lt;p>Kubernetes是天然可作为微服务的地址注册中心，类似于Zookeeper， 阿里巴巴内部用到的VIPserver，Configserver。 具体来说，Kubernetes中的Pod是对于应用的运行实例，Pod的被调度部署/启停都会调用API-Server的服务来保持其状态到ETCD；Kubernetes中的service是对应微服务的概念，定义如下&lt;/p>
&lt;blockquote>
&lt;p>A Kubernetes Service is an abstraction layer which defines a logical set of Pods and enables external traffic exposure, load balancing and service discovery for those Pods.&lt;/p></description></item><item><title>Dubbo Mesh ｜ Service Mesh的实践与探索</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/09/20/dubbo-mesh-service-mesh%E7%9A%84%E5%AE%9E%E8%B7%B5%E4%B8%8E%E6%8E%A2%E7%B4%A2/</link><pubDate>Thu, 20 Sep 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/09/20/dubbo-mesh-service-mesh%E7%9A%84%E5%AE%9E%E8%B7%B5%E4%B8%8E%E6%8E%A2%E7%B4%A2/</guid><description>&lt;p>&lt;img alt="img" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/meetup-chengdu/all-hands.webp">&lt;/p>
&lt;p>近日，在Aliware Open Source•成都站-Apache Dubbo 开发者沙龙上，阿里巴巴中间件高级技术专家李云（至简）向开发者们分享了阿里巴巴中间件团队在Service Mmesh领域的探索和最新实践。本文是根据至简的现场分享所整理，为大家回顾分享中的精彩内容。&lt;/p>
&lt;h2 id="精彩观点导读">精彩观点导读&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>我们去探索一项技术，并不会仅仅因为其先进性，而是因为我们目前遇到了一些无法解决的问题，而这项技术正好能解决这个问题。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>所有软件最重要的使命不是满足功能要求，而是演进，从而持续成长。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>微服务本质是对服务的拆分，微服务架构符合工程领域常用的“分而治之”范式。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="前言">前言&lt;/h2>
&lt;p>我们去探索一项技术，并不会仅仅因为其先进性，而是因为我们目前遇到了一些无法解决的问题，而这项技术正好能解决这个问题。现在，阿里巴巴整个集团业务的体量很大，在技术上会遇到很多的挑战。而正是因为这些挑战，让我们思考通过哪些新技术可以去解决这些痛点，这也是我们在Service Mesh领域进行探索和实践的出发点。首先，我们先来看看自己遇到了哪些挑战。&lt;/p>
&lt;h2 id="微服务的5大挑战">微服务的5大挑战&lt;/h2>
&lt;h4 id="挑战一微服务框架自身演进困难">挑战一：微服务框架自身演进困难。&lt;/h4>
&lt;p>任何软件都会有他的生命进化曲线，从最初的萌芽，进入形成期，往上发展，再进入平台期，最后进入衰亡期。当然我们希望我们的软件可以在进入平台期后，能借助某次演进进入新的发展期。从这个维度看，所有软件最重要的使命不是满足功能要求，而是演进，从而持续成长。相反，当某个软件无法演进的时候，就会意味着死亡。但软件的演进并不是一个简单的事情，以微服务框架为例，为了进一步提升双11期间整个中间件平台的稳定性，我们会修改若干个功能，并以SDK的方式去提供给业务方，但业务代码和微服务框架SDK是强耦合的，这时候需要我们推动各个业务方和我们一同去做升级。虽然我们的初衷是实现平台稳定性的提升，帮助业务更好的发展，但这时由于大家的出发点和诉求有所不同，业务方和我们一起去做升级是比较困难的。所以要发展微服务框架，首先遇到的挑战就是演进困难。
&lt;img alt="img" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/meetup-chengdu/challenges.jpg">&lt;/p>
&lt;h4 id="挑战二微服务框架sdk多语言并行开发与维护成本高">挑战二：微服务框架SDK多语言并行开发与维护成本高。&lt;/h4>
&lt;p>以前我们都是通过对技术栈的统一来提升成本优势和团队效率，大家可以用一种语言去开发和维护，避免多语言时团队的不聚焦。但在软件和开源生态演进的过程中，多语言已经成为一种流行，因为不同语言都有其自身的优势，今天大家能看到的一个现象是云原生的生态中有多种开发语言，使用频率最高的语言已经不是Java了，而是Go，是因为Go的footprint很小。再以 Dubbo为例，除了Java，我们还提供C++，Node.js的SDK，以便让更多的开发者可以加入Dubbo生态，但所有的这些，如果没有社区力量的参与，是很难维持的。
&lt;img alt="img" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/meetup-chengdu/speaker.webp">&lt;/p>
&lt;h4 id="挑战三异构服务框架难以共存完成渐进式演进">挑战三：异构服务框架难以共存完成渐进式演进。&lt;/h4>
&lt;p>我们结合场景来看看这个挑战。阿里巴巴收购了一些企业，被收购企业的技术栈可能和阿里巴巴不同，比如有些用的是Go语言，有些用的是PHP，这时候为了统一技术栈，我们需要对这类技术平台推倒重来，但这个过程中，我们会面临一系列问题，首当其冲的就是推倒重来会带来巨大的技术风险，其次是可能会面临技术人员大批量流失的风险，这在社会责任的层面也是很难接受。所以我们在寻求一种可能的方案，去解决这类问题。&lt;/p>
&lt;h4 id="挑战四是单一的语言限制了人才的多样性">挑战四：是单一的语言限制了人才的多样性。&lt;/h4>
&lt;p>这里，我们不去争论某个编程语言的好与坏，每个语言都有其适用场景，你不能说我手里有个榔头，你面对的都是钉子。以前我们觉得统一技术栈可以集中开发力量，并且带来较高的运维便利性。但伴随着互联网带来的快节奏，以往的团队能力设置已经很难满足这类变化，对工程师个体提出了更高的要求，我们不仅仅需要是某一方面的专家，而且还需要具备多域的工作技能，DevOps和全栈工程师就是这类快节奏变化下最好的注脚。
&lt;img alt="img" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/meetup-chengdu/challenges-continued.jpg">&lt;/p>
&lt;h4 id="挑战五是点状的服务治理难以做到及时有效和经济">挑战五：是点状的服务治理难以做到及时、有效和经济。&lt;/h4>
&lt;p>微服务和架构的核心是拆分，通过拆分，让每个模块可以独立运行，跟上业务的发展速度，持续推动业务的创新。但拆完后新的问题出来了，缺少横向的内容拉通所有独立的烟囱，从而在服务治理上带来极大的挑战。&lt;/p>
&lt;h2 id="分布式应用的发展趋势">分布式应用的发展趋势&lt;/h2>
&lt;p>微服务会成为大规模分布式应用的主流架构。任何复杂的工程问题都会归结为devide and conquer（分而治之），意思就是就是把一个复杂的问题分成两个或更多的相同或相似的子问题，再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解，原问题的解即子问题的解的合并。微服务本质是对服务的拆分，与工程领域惯用的“分而治之”的思路是一致的。&lt;/p>
&lt;p>微服务架构下应用的开发是多语言的。没有一个语言是一家独大的，每种语言在特定场景下都有其自身的优势，我们希望这种优势能够将技术到产品的周期（time to market）缩短。技术的核心在于创造价值，无论是交付给客户，还是服务于整个社会。因此，微服务是需要不同语言的开发者发挥自身的优势，去进一步完善我们的微服务架构，释放技术价值。
&lt;img alt="img" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/meetup-chengdu/trends.jpg">&lt;/p>
&lt;p>数据安全将成为公有云分布式应用的生命线。云原生时代，业务即便没上云，企业对自身数据的安全都是有诉求的，尤其是在金融行业，如果通过抓包就能获取一些敏感信息，这将会给企业带来巨大的风险。&lt;/p>
&lt;p>Cloud native成为distributionless（无分布式）的主要探索路径。分布式发展的终极形式是无分布式，在未来我们做开发，所有的代码在web上写好后，通过点击一个按钮，所有部署都会自动实现，所有的code review的工作可以在一个统一的工作台上全部实现。
&lt;img alt="img" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/meetup-chengdu/audience-shapshot.webp">&lt;/p>
&lt;p>以更快的速度，通过构建软件去探索新业务。工程师服务的是客户，通过技术输出来实现技术价值，以互联网的架构帮助赋能传统企业，帮助企业获得差异化竞争力。&lt;/p>
&lt;h2 id="什么是service-mesh">什么是Service Mesh&lt;/h2>
&lt;p>Service Mesh是层次化、规范化、体系化、无侵入的分布式服务治理技术平台。&lt;/p>
&lt;h4 id="层次化">层次化&lt;/h4>
&lt;p>分为数据面和控制面两个概念，数据面是指所有数据流动的那个层面，控制面是用来控制这个数据面的，对服务去做处理。对数据面和控制面进行分层，带来的好处是，针对一个复杂的系统进行切分，可以获得更清晰的认识，这和devide and conque是同一个理念。&lt;/p>
&lt;h4 id="规范化">规范化&lt;/h4>
&lt;p>是指通过标准协议完成数据平面和控制平面的连接，同时，sidecar成为所有traffic互联、互通的约束标准。&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/meetup-chengdu/what.jpg">&lt;/p>
&lt;h4 id="体系化">体系化&lt;/h4>
&lt;p>包含两个维度，一是指observability全局考虑。目前在整个分布式治理过程中的最大挑战是：logging、metrics、tracing这三个observability领域的核心内容缺少体系性的关注。另一个是集中管理的维度，包括服务管理、限流、熔断、安全、灰度在内的服务模块都可以在获得体系化的呈现，每个服务都可以被看到，而非团队a只看限流，团队b只看logging，需要一种技术能力拉通所有的服务模块，这个体系化这个角度看，Service Mesh是一个理想的技术方案。&lt;/p>
&lt;h4 id="无侵入">无侵入&lt;/h4>
&lt;p>是指我们希望通过无侵入，当新增一个业务的时候，不需要考虑一个SDK去初始化，而是可以通过sidecar的进程方式来解耦。&lt;/p>
&lt;h2 id="service-mesh的形态">Service Mesh的形态&lt;/h2>
&lt;p>我们从三个维度对比的来看 ServiceMesh 的形态。&lt;/p>
&lt;p>图中左边是传统的微服务形态，调用者和被调用者是通过一个SDK的方式来实现共享服务的，以Dubbo为例，我们会在SDK里提供服务路由、服务发现等功能，虽然我们的开发者在做应用开发的时候并不会太关注SDK的构成，但这些功能是面临不断被变更的可能，有着比较重的逻辑。在右边Service Mesh的形态中，我们首先会对厚重的SDK进行分解，将复杂的逻辑下沉到sidecar，借助sidecar来实现服务的调用。&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/meetup-chengdu/forms.jpg">&lt;/p>
&lt;p>虽然在Service Mesh的形态，调用路径要长于传统的形态，路径越长消耗越大，对性能影响越大。但在当前的分布式应用的治理过程中，易用性已经成为一个比性能更重要的话题。当我们给客户部署一套微服务，即便性能很强，但没有处理好易用性问题的话，这将会给技术的推广带来巨大的阻碍，不仅是会影响外部的客户，也会影响内部的用户，如何实现喝着咖啡从容应对双11，必须先解决易用性的问题。在解决易用性问题后，沿着技术的发展路径再去解决性能问题。&lt;/p>
&lt;p>Service Mesh的形态中的control plan不会导致重复建设，但在shared service是有可能存在重复建设的。&lt;/p>
&lt;h2 id="service-mesh下的应用架构">Service Mesh下的应用架构&lt;/h2>
&lt;p>无论是单体应用，还是分布式应用，都可以建立在Service Mesh上，mesh上的sidecar支撑了所有的上层应用，业务开发者无须关心底层构成，可以用Java，也可以用Go等语言完成自己的业务开发。&lt;/p></description></item><item><title>如何基于Dubbo实现全异步调用链</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/09/02/%E5%A6%82%E4%BD%95%E5%9F%BA%E4%BA%8Edubbo%E5%AE%9E%E7%8E%B0%E5%85%A8%E5%BC%82%E6%AD%A5%E8%B0%83%E7%94%A8%E9%93%BE/</link><pubDate>Sun, 02 Sep 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/09/02/%E5%A6%82%E4%BD%95%E5%9F%BA%E4%BA%8Edubbo%E5%AE%9E%E7%8E%B0%E5%85%A8%E5%BC%82%E6%AD%A5%E8%B0%83%E7%94%A8%E9%93%BE/</guid><description>&lt;p>基于Dubbo实现全异步编程，是在2.7.0版本中对现有异步方式增强后新引入的功能。本文先是回顾2.6.x及之前版本对异步的支持情况及存在的问题，引出了2.7.0版本基于CompletableFuture做了哪些针对性的增强，通过几个示例详细阐述了增强后的异步编程的使用方式，最后总结了引入异步模式带来的新问题及Dubbo的解决方法。通过阅读这篇文章，可以很容易的基于Dubbo2.7.0+版本实现一个全异步的远程服务调用链路。&lt;/p>
&lt;p>从3.0.0版本开始，Dubbo框架提供了对Reactive编程范式的支持，除了编程接口之外，在跨进程的RPC通信中引入了Reactive的语义。如果你所在的环境需要使用Reactive编程范式，或者你的RPC调用需要支持流式传输，Reactive应该会给你带来帮助，具体请参考发布在阿里巴巴中间件公众号上的响应式编程支持相关文章。&lt;/p>
&lt;blockquote>
&lt;p>注意，你可能并不是总需要Reactive的语义，尤其是在RPC的场景，CompletableFuture本身也能带给你Reactive模式的编程模型，在选择Reactive（RxJava、Reactor之类）而不是理解及使用成本更低的CompletableFuture前，请尝试关注以下问题：&lt;/p>
&lt;ol>
&lt;li>你是请求/响应是一次性传输的还是流式传输的，一个明显特征是你定义的数据类型是 &lt;code>List&amp;lt;String&amp;gt;&lt;/code> 还是 &lt;code>Stream&amp;lt;String&amp;gt;&lt;/code>&lt;/li>
&lt;li>你的RPC请求有没有要求是Cold，即在subscribe后触发，因为CompletableFuture总是hot的&lt;/li>
&lt;li>你依赖的编程上下文中是否已经在大量使用Reactive的编程接口&lt;/li>
&lt;li>你是否需要Rx框架提供的更丰富的Operator，而这点和1又是密切相关的&lt;/li>
&lt;/ol>
&lt;/blockquote>
&lt;h2 id="26x版本之前的异步方式">2.6.x版本之前的异步方式&lt;/h2>
&lt;p>在2.6.x及之前的版本提供了一定的异步编程能力，包括Consumer端&lt;a href="https://deploy-preview-3202--dubbo.netlify.app/zh-cn/docsv2.7/user/examples/async-call/">异步调用&lt;/a>、&lt;a href="https://deploy-preview-3202--dubbo.netlify.app/zh-cn/docsv2.7/user/examples/callback-parameter/">参数回调&lt;/a>、&lt;a href="https://deploy-preview-3202--dubbo.netlify.app/zh-cn/docsv2.7/user/examples/events-notify/">事件通知&lt;/a>等，在上面的文档链接中有关于使用方式的简单介绍和Demo。&lt;/p>
&lt;p>关于参数回调，其本质上是一种服务端的数据推送能力，这是终端应用很常见的一种需求，关于这部分的重构计划，不在本文讨论范围。&lt;/p>
&lt;p>但当前的异步方式存在以下问题：&lt;/p>
&lt;ul>
&lt;li>Future获取方式不够直接&lt;/li>
&lt;li>Future接口无法实现自动回调，而自定义ResponseFuture虽支持回调但支持的异步场景有限，如不支持Future间的相互协调或组合等&lt;/li>
&lt;li>不支持Provider端异步&lt;/li>
&lt;/ul>
&lt;p>以Consumer端异步使用方式为例：&lt;/p>
&lt;ol>
&lt;li>定义一个普通的同步接口并声明支持异步调用&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">FooService&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">findFoo&lt;/span>(String name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;fooService&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;com.alibaba.foo.FooService&amp;#34;&lt;/span>&lt;span style="color:#268bd2">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo:method&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;findFoo&amp;#34;&lt;/span> async=&lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span> &lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/dubbo:reference&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>通过RpcContext获取Future&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 此调用会立即返回null&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>fooService.findFoo(fooId);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 拿到调用的Future引用，当结果返回后，会被通知和设置到此Future&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Future&lt;span style="color:#719e07">&amp;lt;&lt;/span>Foo&lt;span style="color:#719e07">&amp;gt;&lt;/span> fooFuture &lt;span style="color:#719e07">=&lt;/span> RpcContext.getContext().getFuture();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>fooFuture.get();
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>或&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 此调用会立即返回null&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>fooService.findFoo(fooId);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 拿到Dubbo内置的ResponseFuture并设置回调&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ResponseFuture future &lt;span style="color:#719e07">=&lt;/span> ((FutureAdapter)RpcContext.getContext().getFuture()).getFuture();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>future.setCallback(&lt;span style="color:#719e07">new&lt;/span> ResponseCallback() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">done&lt;/span>(Object response) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.print(response);
&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">caught&lt;/span>(Throwable exception) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exception.printStackTrace();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>});
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>从这个简单的示例我们可以体会到一些使用中的不便之处：&lt;/p></description></item><item><title>Dubbo 集群容错</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/22/dubbo-%E9%9B%86%E7%BE%A4%E5%AE%B9%E9%94%99/</link><pubDate>Wed, 22 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/22/dubbo-%E9%9B%86%E7%BE%A4%E5%AE%B9%E9%94%99/</guid><description>&lt;h3 id="design-for-failure">Design For failure&lt;/h3>
&lt;p>在分布式系统中，集群某个某些节点出现问题是大概率事件，因此在设计分布式RPC框架的过程中，必须要把失败作为设计的一等公民来对待。一次调用失败之后，应该如何选择对失败的选择策略，这是一个见仁见智的问题，每种策略可能都有自己独特的应用场景。因此，作为框架来说，应当针对不同场景提供多种策略，供用户进行选择。&lt;/p>
&lt;p>在Dubbo设计中，通过Cluster这个接口的抽象，把一组可供调用的Provider信息组合成为一个统一的&lt;code>Invoker&lt;/code>供调用方进行调用。经过路由规则过滤，负载均衡选址后，选中一个具体地址进行调用，如果调用失败，则会按照集群配置的容错策略进行容错处理。&lt;/p>
&lt;p>Dubbo默认内置了若干容错策略，如果不能满足用户需求，则可以通过自定义容错策略进行配置。&lt;/p>
&lt;h3 id="内置容错策略">内置容错策略&lt;/h3>
&lt;p>Dubbo主要内置了如下几种策略：&lt;/p>
&lt;ul>
&lt;li>Failover(失败自动切换)&lt;/li>
&lt;li>Failsafe(失败安全)&lt;/li>
&lt;li>Failfast(快速失败)&lt;/li>
&lt;li>Failback(失败自动恢复)&lt;/li>
&lt;li>Forking(并行调用)&lt;/li>
&lt;li>Broadcast(广播调用)&lt;/li>
&lt;/ul>
&lt;p>这些名称比较相似，概念也比较容易混淆，下面逐一进行解释。&lt;/p>
&lt;h4 id="failover失败自动切换">Failover(失败自动切换)&lt;/h4>
&lt;p>&lt;code>Failover&lt;/code>是高可用系统中的一个常用概念，服务器通常拥有主备两套机器配置，如果主服务器出现故障，则自动切换到备服务器中，从而保证了整体的高可用性。&lt;/p>
&lt;p>Dubbo也借鉴了这个思想，并且把它作为Dubbo&lt;code>默认的容错策略&lt;/code>。当调用出现失败的时候，根据配置的重试次数，会自动从其他可用地址中重新选择一个可用的地址进行调用，直到调用成功，或者是达到重试的上限位置。&lt;/p>
&lt;p>Dubbo里默认配置的重试次数是2，也就是说，算上第一次调用，最多会调用3次。&lt;/p>
&lt;p>其配置方法，容错策略既可以在服务提供方配置，也可以服务调用方进行配置。而重试次数的配置则更为灵活，既可以在服务级别进行配置，也可以在方法级别进行配置。具体优先顺序为：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>服务调用方方法级配置 &amp;gt; 服务调用方服务级配置 &amp;gt; 服务提供方方法级配置 &amp;gt; 服务提供方服务级配置
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>以XML方式为例，具体配置方法如下：&lt;/p>
&lt;p>服务提供方，服务级配置&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:service&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.demo.DemoService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span> cluster=&lt;span style="color:#2aa198">&amp;#34;failover&amp;#34;&lt;/span> retries=&lt;span style="color:#2aa198">&amp;#34;2&amp;#34;&lt;/span> &lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>服务提供方，方法级配置&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:service&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.demo.DemoService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span>cluster=&lt;span style="color:#2aa198">&amp;#34;failover&amp;#34;&lt;/span>&lt;span style="color:#268bd2">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo:method&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;sayHello&amp;#34;&lt;/span> retries=&lt;span style="color:#2aa198">&amp;#34;2&amp;#34;&lt;/span> &lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/dubbo:reference&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>服务调用方，服务级配置&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.demo.DemoService&amp;#34;&lt;/span> cluster=&lt;span style="color:#2aa198">&amp;#34;failover&amp;#34;&lt;/span> retries=&lt;span style="color:#2aa198">&amp;#34;1&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>服务调用方，方法级配置：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.demo.DemoService&amp;#34;&lt;/span> cluster=&lt;span style="color:#2aa198">&amp;#34;failover&amp;#34;&lt;/span>&lt;span style="color:#268bd2">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo:method&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;sayHello&amp;#34;&lt;/span> retries=&lt;span style="color:#2aa198">&amp;#34;3&amp;#34;&lt;/span> &lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/dubbo:reference&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Failover可以自动对失败进行重试，对调用者屏蔽了失败的细节，但是Failover策略也会带来一些副作用：&lt;/p>
&lt;ul>
&lt;li>重试会额外增加一下开销，例如增加资源的使用，在高负载系统下，额外的重试可能让系统雪上加霜。&lt;/li>
&lt;li>重试会增加调用的响应时间。&lt;/li>
&lt;li>某些情况下，重试甚至会造成资源的浪费。考虑一个调用场景，A-&amp;gt;B-&amp;gt;C，如果A处设置了超时100ms，再B-&amp;gt;C的第一次调用完成时已经超过了100ms，但很不幸B-&amp;gt;C失败，这时候会进行重试，但其实这时候重试已经没有意义，因此在A看来这次调用已经超时，A可能已经开始执行其他逻辑。&lt;/li>
&lt;/ul>
&lt;h4 id="failsafe失败安全">Failsafe(失败安全)&lt;/h4>
&lt;p>失败安全策略的核心是即使失败了也不会影响整个调用流程。通常情况下用于旁路系统或流程中，它的失败不影响核心业务的正确性。在实现上，当出现调用失败时，会忽略此错误，并记录一条日志，同时返回一个空结果，在上游看来调用是成功的。&lt;/p>
&lt;p>应用场景，可以用于写入审计日志等操作。&lt;/p>
&lt;p>具体配置方法：&lt;/p>
&lt;p>服务提供方，服务级配置&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:service&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.demo.DemoService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span> cluster=&lt;span style="color:#2aa198">&amp;#34;failsafe&amp;#34;&lt;/span> &lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>服务调用方，服务级配置&lt;/p></description></item><item><title>Dubbo 现有心跳方案总结以及改进建议</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/19/dubbo-%E7%8E%B0%E6%9C%89%E5%BF%83%E8%B7%B3%E6%96%B9%E6%A1%88%E6%80%BB%E7%BB%93%E4%BB%A5%E5%8F%8A%E6%94%B9%E8%BF%9B%E5%BB%BA%E8%AE%AE/</link><pubDate>Sun, 19 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/19/dubbo-%E7%8E%B0%E6%9C%89%E5%BF%83%E8%B7%B3%E6%96%B9%E6%A1%88%E6%80%BB%E7%BB%93%E4%BB%A5%E5%8F%8A%E6%94%B9%E8%BF%9B%E5%BB%BA%E8%AE%AE/</guid><description>&lt;h3 id="1-前言">1 前言&lt;/h3>
&lt;p>设计一个好的心跳机制并不是一件容易的事，就我所熟知的几个 RPC 框架，它们的心跳机制可以说大相径庭，这篇文章我将探讨一下&lt;strong>如何设计一个优雅的心跳机制，主要从 Dubbo 的现有方案以及一个改进方案来做分析&lt;/strong>。&lt;/p>
&lt;h3 id="2-预备知识">2 预备知识&lt;/h3>
&lt;p>因为后续我们将从源码层面来进行介绍，所以一些服务治理框架的细节还需要提前交代一下，方便大家理解。&lt;/p>
&lt;h4 id="21-客户端如何得知请求失败了">2.1 客户端如何得知请求失败了？&lt;/h4>
&lt;p>高性能的 RPC 框架几乎都会选择使用 Netty 来作为通信层的组件，非阻塞式通信的高效不需要我做过多的介绍。但也由于非阻塞的特性，导致其发送数据和接收数据是一个异步的过程，所以当存在服务端异常、网络问题时，客户端是接收不到响应的，那么我们如何判断一次 RPC 调用是失败的呢？&lt;/p>
&lt;p>误区一：Dubbo 调用不是默认同步的吗？&lt;/p>
&lt;p>Dubbo 在通信层是异步的，呈现给使用者同步的错觉是因为内部做了阻塞等待，实现了异步转同步。&lt;/p>
&lt;p>误区二： &lt;code>Channel.writeAndFlush&lt;/code> 会返回一个 &lt;code>channelFuture&lt;/code>，我只需要判断 &lt;code>channelFuture.isSuccess&lt;/code> 就可以判断请求是否成功了。&lt;/p>
&lt;p>注意，writeAndFlush 成功并不代表对端接受到了请求，返回值为 true 只能保证写入网络缓冲区成功，并不代表发送成功。&lt;/p>
&lt;p>避开上述两个误区，我们再来回到本小节的标题：客户端如何得知请求失败？&lt;strong>正确的逻辑应当是以客户端接收到失败响应为判断依据&lt;/strong>。等等，前面不还在说在失败的场景中，服务端是不会返回响应的吗？没错，既然服务端不会返回，那就只能客户端自己造了。&lt;/p>
&lt;p>一个常见的设计是：客户端发起一个 RPC 请求，会设置一个超时时间 &lt;code>client_timeout&lt;/code>，发起调用的同时，客户端会开启一个延迟 &lt;code>client_timeout&lt;/code> 的定时器&lt;/p>
&lt;ul>
&lt;li>接收到正常响应时，移除该定时器。&lt;/li>
&lt;li>定时器倒计时完毕，还没有被移除，则认为请求超时，构造一个失败的响应传递给客户端。&lt;/li>
&lt;/ul>
&lt;p>Dubbo 中的超时判定逻辑：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> DefaultFuture &lt;span style="color:#268bd2">newFuture&lt;/span>(Channel channel, Request request, &lt;span style="color:#dc322f">int&lt;/span> timeout) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">final&lt;/span> DefaultFuture future &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> DefaultFuture(channel, request, timeout);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// timeout check&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> timeoutCheck(future);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> future;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">timeoutCheck&lt;/span>(DefaultFuture future) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> TimeoutCheckTask task &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> TimeoutCheckTask(future);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> TIME_OUT_TIMER.newTimeout(task, future.getTimeout(), TimeUnit.MILLISECONDS);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">TimeoutCheckTask&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> TimerTask {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> DefaultFuture future;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> TimeoutCheckTask(DefaultFuture future) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>.future &lt;span style="color:#719e07">=&lt;/span> future;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">run&lt;/span>(Timeout timeout) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (future &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">||&lt;/span> future.isDone()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// create exception response.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Response timeoutResponse &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Response(future.getId());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// set timeout status.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> timeoutResponse.setStatus(future.isSent() &lt;span style="color:#719e07">?&lt;/span> Response.SERVER_TIMEOUT : Response.CLIENT_TIMEOUT);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> timeoutResponse.setErrorMessage(future.getTimeoutMessage(&lt;span style="color:#cb4b16">true&lt;/span>));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// handle response.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> DefaultFuture.received(future.getChannel(), timeoutResponse);
&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>DubboInvoker&lt;/code>，&lt;code>HeaderExchangeChannel&lt;/code>，&lt;code>DefaultFuture&lt;/code> ，通过上述代码，我们可以得知一个细节，无论是何种调用，都会经过这个定时器的检测，&lt;strong>超时即调用失败，一次 RPC 调用的失败，必须以客户端收到失败响应为准&lt;/strong>。&lt;/p></description></item><item><title>Dubbo2.7 三大新特性详解</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/15/dubbo2.7-%E4%B8%89%E5%A4%A7%E6%96%B0%E7%89%B9%E6%80%A7%E8%AF%A6%E8%A7%A3/</link><pubDate>Wed, 15 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/15/dubbo2.7-%E4%B8%89%E5%A4%A7%E6%96%B0%E7%89%B9%E6%80%A7%E8%AF%A6%E8%A7%A3/</guid><description>&lt;h2 id="1-背景介绍">1 背景介绍&lt;/h2>
&lt;p>自 2017 年 7 月阿里重启 Dubbo 开源，到目前为止 github star 数，contributor 数都有了非常大的提升。2018 年 2 月 9 日阿里决定将 Dubbo 项目贡献给 Apache，经过一周的投票，顺利成为了 Apache 的孵化项目，也就是大家现在看到的 &lt;strong>Incubator Dubbo&lt;/strong>。预计在 2019 年 4 月，Dubbo 可以达成毕业，成为 Apache 的顶级项目。&lt;/p>
&lt;h2 id="2-分支介绍">2 分支介绍&lt;/h2>
&lt;p>&lt;img alt="分支" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/270/branches.png">&lt;/p>
&lt;p>Dubbo 目前有如图所示的 5 个分支，其中 2.7.1-release 只是一个临时分支，忽略不计，对其他 4 个分支进行介绍。&lt;/p>
&lt;ul>
&lt;li>2.5.x 近期已经通过投票，Dubbo 社区即将停止对其的维护。&lt;/li>
&lt;li>2.6.x 为长期支持的版本，也是 Dubbo 贡献给 Apache 之前的版本，其包名前缀为：com.alibaba，JDK 版本对应 1.6。&lt;/li>
&lt;li>3.x-dev 是前瞻性的版本，对 Dubbo 进行一些高级特性的补充，如支持 rx 特性。&lt;/li>
&lt;li>master 为长期支持的版本，版本号为 2.7.x，也是 Dubbo 贡献给 Apache 的开发版本，其包名前缀为：org.apache，JDK 版本对应 1.8。&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>如果想要研究 Dubbo 的源码，建议直接浏览 master 分支。&lt;/p></description></item><item><title>Dubbo 关于同步/异步调用的几种方式</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/14/dubbo-%E5%85%B3%E4%BA%8E%E5%90%8C%E6%AD%A5/%E5%BC%82%E6%AD%A5%E8%B0%83%E7%94%A8%E7%9A%84%E5%87%A0%E7%A7%8D%E6%96%B9%E5%BC%8F/</link><pubDate>Tue, 14 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/14/dubbo-%E5%85%B3%E4%BA%8E%E5%90%8C%E6%AD%A5/%E5%BC%82%E6%AD%A5%E8%B0%83%E7%94%A8%E7%9A%84%E5%87%A0%E7%A7%8D%E6%96%B9%E5%BC%8F/</guid><description>&lt;p>我们知道，Dubbo 缺省协议采用单一长连接，底层实现是 Netty 的 NIO 异步通讯机制；基于这种机制，Dubbo 实现了以下几种调用方式：&lt;/p>
&lt;ul>
&lt;li>同步调用&lt;/li>
&lt;li>异步调用&lt;/li>
&lt;li>参数回调&lt;/li>
&lt;li>事件通知&lt;/li>
&lt;/ul>
&lt;h2 id="同步调用">同步调用&lt;/h2>
&lt;p>同步调用是一种阻塞式的调用方式，即 Consumer 端代码一直阻塞等待，直到 Provider 端返回为止；&lt;/p>
&lt;p>通常，一个典型的同步调用过程如下：&lt;/p>
&lt;ol>
&lt;li>Consumer 业务线程调用远程接口，向 Provider 发送请求，同时当前线程处于&lt;code>阻塞&lt;/code>状态；&lt;/li>
&lt;li>Provider 接到 Consumer 的请求后，开始处理请求，将结果返回给 Consumer；&lt;/li>
&lt;li>Consumer 收到结果后，当前线程继续往后执行。&lt;/li>
&lt;/ol>
&lt;p>这里有 2 个问题：&lt;/p>
&lt;ol>
&lt;li>Consumer 业务线程是怎么进入&lt;code>阻塞&lt;/code>状态的？&lt;/li>
&lt;li>Consumer 收到结果后，如何唤醒业务线程往后执行的？&lt;/li>
&lt;/ol>
&lt;p>其实，Dubbo 的底层 IO 操作都是异步的。Consumer 端发起调用后，得到一个 Future 对象。对于同步调用，业务线程通过&lt;code>Future#get(timeout)&lt;/code>，阻塞等待 Provider 端将结果返回；&lt;code>timeout&lt;/code>则是 Consumer 端定义的超时时间。当结果返回后，会设置到此 Future，并唤醒阻塞的业务线程；当超时时间到结果还未返回时，业务线程将会异常返回。&lt;/p>
&lt;h2 id="异步调用">异步调用&lt;/h2>
&lt;p>基于 Dubbo 底层的异步 NIO 实现异步调用，对于 Provider 响应时间较长的场景是必须的，它能有效利用 Consumer 端的资源，相对于 Consumer 端使用多线程来说开销较小。&lt;/p>
&lt;p>异步调用，对于 Provider 端不需要做特别的配置。下面的例子中，Provider 端接口定义如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">AsyncService&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">goodbye&lt;/span>(String name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="consumer-配置">Consumer 配置&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;asyncService&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.samples.async.api.AsyncService&amp;#34;&lt;/span>&lt;span style="color:#268bd2">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo:method&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;goodbye&amp;#34;&lt;/span> async=&lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/dubbo:reference&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>需要异步调用的方法，均需要使用 &lt;code>&amp;lt;dubbo:method/&amp;gt;&lt;/code>标签进行描述。&lt;/p></description></item><item><title>Dubbo 基本用法 - Dubbo Consumer 配置</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/14/dubbo-%E5%9F%BA%E6%9C%AC%E7%94%A8%E6%B3%95-dubbo-consumer-%E9%85%8D%E7%BD%AE/</link><pubDate>Tue, 14 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/14/dubbo-%E5%9F%BA%E6%9C%AC%E7%94%A8%E6%B3%95-dubbo-consumer-%E9%85%8D%E7%BD%AE/</guid><description>&lt;h2 id="dubbo-consumer配置">Dubbo Consumer配置&lt;/h2>
&lt;h3 id="consumer配置详解">Consumer配置详解&lt;/h3>
&lt;p>配置Dubbo Consumer有3种方式：XML配置，API调用方式配置，注解方式配置。&lt;/p>
&lt;h4 id="xml配置">XML配置&lt;/h4>
&lt;h6 id="最简单的配置的样例">最简单的配置的样例：&lt;/h6>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;beans xmlns=&amp;#34;http://www.springframework.org/schema/beans&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> xmlns:xsi=&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> xmlns:dubbo=&amp;#34;http://dubbo.apache.org/schema/dubbo&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> xsi:schemaLocation=&amp;#34;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd&amp;#34;&amp;gt; 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;dubbo:application name=&amp;#34;hello-world-app&amp;#34; /&amp;gt; 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;dubbo:registry address=&amp;#34;multicast://224.5.6.7:1234&amp;#34; /&amp;gt; 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;dubbo:protocol name=&amp;#34;dubbo&amp;#34; port=&amp;#34;20880&amp;#34; /&amp;gt; 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;dubbo:reference id=&amp;#34;demoServiceRemote&amp;#34; interface=&amp;#34;com.alibaba.dubbo.demo.DemoService&amp;#34; /&amp;gt; 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;/beans&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>支持的配置标签及对应的配置项详解，参考provider中的用法。&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>接下来重点讲解下&amp;lt;dubbo:reference/&amp;gt;的配置。&lt;/p>
&lt;/blockquote>
&lt;ul>
&lt;li>&amp;lt;dubbo:reference/&amp;gt;支持的主要属性列表：&lt;/li>
&lt;/ul>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th style="text-align: left">属性名&lt;/th>
 &lt;th style="text-align: left">说明&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td style="text-align: left">id&lt;/td>
 &lt;td style="text-align: left">服务引用id，作为java bean id，需要唯一&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">interface&lt;/td>
 &lt;td style="text-align: left">接口名，用于查找服务&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">version&lt;/td>
 &lt;td style="text-align: left">版本号，与服务提供者的版本一致&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">timeout&lt;/td>
 &lt;td style="text-align: left">服务方法调用超时时间(毫秒)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">retries&lt;/td>
 &lt;td style="text-align: left">远程服务调用重试次数，不包括第一次调用，不需要重试请设为0&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">connections&lt;/td>
 &lt;td style="text-align: left">对每个提供者的最大连接数，rmi、http、hessian等短连接协议表示限制连接数，dubbo等长连接协表示建立的长连接个数&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">loadbalance&lt;/td>
 &lt;td style="text-align: left">负载均衡策略，可选值：random,roundrobin,leastactive，分别表示：随机，轮询，最少活跃调用&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">async&lt;/td>
 &lt;td style="text-align: left">是否异步执行，不可靠异步，只是忽略返回值，不阻塞执行线程&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">generic&lt;/td>
 &lt;td style="text-align: left">泛化调用，可以绕过&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">check&lt;/td>
 &lt;td style="text-align: left">启动时检查提供者是否存在，true报错，false忽略&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">actives&lt;/td>
 &lt;td style="text-align: left">每服务消费者每服务每方法最大并发调用数&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>其他配置属性请参考xsd：http://dubbo.apache.org/schema/dubbo/dubbo.xsd&lt;/p></description></item><item><title>Dubbo 基础用法 - Provider 配置</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/14/dubbo-%E5%9F%BA%E7%A1%80%E7%94%A8%E6%B3%95-provider-%E9%85%8D%E7%BD%AE/</link><pubDate>Tue, 14 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/14/dubbo-%E5%9F%BA%E7%A1%80%E7%94%A8%E6%B3%95-provider-%E9%85%8D%E7%BD%AE/</guid><description>&lt;h2 id="dubbo基本用法">Dubbo基本用法&lt;/h2>
&lt;p>本章节主要讲述如何配置dubbo，按照配置方式上分，可以分为：XML配置，properties方式配置，注解方式配置，API调用方式配置。
按照功能角度进行划分，可以分为Dubbo Provider和Dubbo Consumer。接下来章节中，分别对dubbo provider和Dubbo consumer进行讲解。&lt;/p>
&lt;h3 id="dubbo-provider配置">Dubbo Provider配置&lt;/h3>
&lt;h4 id="provider-配置详解">Provider 配置详解&lt;/h4>
&lt;p>配置Dubbo Provider有4种方式：XML配置，properties方式配置，API调用方式配置，注解方式配置。&lt;/p>
&lt;h5 id="xml配置">XML配置&lt;/h5>
&lt;h6 id="最简单的配置的样例">最简单的配置的样例：&lt;/h6>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;beans xmlns=&amp;#34;http://www.springframework.org/schema/beans&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> xmlns:xsi=&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> xmlns:dubbo=&amp;#34;http://dubbo.apache.org/schema/dubbo&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> xsi:schemaLocation=&amp;#34;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd&amp;#34;&amp;gt; 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;dubbo:application name=&amp;#34;hello-world-app&amp;#34; /&amp;gt; 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;dubbo:registry address=&amp;#34;multicast://224.5.6.7:1234&amp;#34; /&amp;gt; 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;dubbo:protocol name=&amp;#34;dubbo&amp;#34; port=&amp;#34;20880&amp;#34; /&amp;gt; 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;dubbo:service interface=&amp;#34;com.alibaba.dubbo.demo.DemoService&amp;#34; ref=&amp;#34;demoServiceLocal&amp;#34; /&amp;gt; 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;dubbo:reference id=&amp;#34;demoServiceRemote&amp;#34; interface=&amp;#34;com.alibaba.dubbo.demo.DemoService&amp;#34; /&amp;gt; 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;/beans&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>上面样例中，注意下dubbo schema的写法：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>&amp;lt;beans xmlns:xsi=&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> xmlns:dubbo=&amp;#34;http://code.alibabatech.com/schema/dubbo&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> xmlns=&amp;#34;http://www.springframework.org/schema/beans&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> xsi:schemaLocation=&amp;#34;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd&amp;#34;&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h6 id="支持的配置标签">支持的配置标签&lt;/h6>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th style="text-align: left">标签&lt;/th>
 &lt;th style="text-align: left">用途&lt;/th>
 &lt;th style="text-align: left">解释&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td style="text-align: left">&amp;lt;dubbo:service/&amp;gt;&lt;/td>
 &lt;td style="text-align: left">服务配置&lt;/td>
 &lt;td style="text-align: left">用于暴露一个服务，定义服务的元信息，一个服务可以用多个协议暴露，一个服务也可以注册到多个注册中心&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&amp;lt;dubbo:reference/&amp;gt;&lt;/td>
 &lt;td style="text-align: left">引用配置&lt;/td>
 &lt;td style="text-align: left">用于创建一个远程服务代理，一个引用可以指向多个注册中心&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&amp;lt;dubbo:protocol/&amp;gt;&lt;/td>
 &lt;td style="text-align: left">协议配置&lt;/td>
 &lt;td style="text-align: left">用于配置提供服务的协议信息，协议由提供方指定，消费方被动接受&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&amp;lt;dubbo:application/&amp;gt;&lt;/td>
 &lt;td style="text-align: left">应用配置&lt;/td>
 &lt;td style="text-align: left">用于配置当前应用信息，不管该应用是提供者还是消费者&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&amp;lt;dubbo:module/&amp;gt;&lt;/td>
 &lt;td style="text-align: left">模块配置&lt;/td>
 &lt;td style="text-align: left">用于配置当前模块信息，可选&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&amp;lt;dubbo:registry/&amp;gt;&lt;/td>
 &lt;td style="text-align: left">注册中心配置&lt;/td>
 &lt;td style="text-align: left">用于配置连接注册中心相关信息&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&amp;lt;dubbo:monitor/&amp;gt;&lt;/td>
 &lt;td style="text-align: left">监控中心配置&lt;/td>
 &lt;td style="text-align: left">用于配置连接监控中心相关信息，可选&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&amp;lt;dubbo:provider/&amp;gt;&lt;/td>
 &lt;td style="text-align: left">提供方配置&lt;/td>
 &lt;td style="text-align: left">当 ProtocolConfig 和 ServiceConfig 某属性没有配置时，采用此缺省值，可选&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&amp;lt;dubbo:consumer/&amp;gt;&lt;/td>
 &lt;td style="text-align: left">消费方配置&lt;/td>
 &lt;td style="text-align: left">当 ReferenceConfig 某属性没有配置时，采用此缺省值，可选&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&amp;lt;dubbo:method/&amp;gt;&lt;/td>
 &lt;td style="text-align: left">方法配置&lt;/td>
 &lt;td style="text-align: left">用于 ServiceConfig 和 ReferenceConfig 指定方法级的配置信息&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&amp;lt;dubbo:argument/&amp;gt;&lt;/td>
 &lt;td style="text-align: left">参数配置&lt;/td>
 &lt;td style="text-align: left">用于指定方法参数配置&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>&lt;img alt="配置之间关系图" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/user/dubbo-config.jpg">&lt;/p></description></item><item><title>Dubbo 优雅停机</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/14/dubbo-%E4%BC%98%E9%9B%85%E5%81%9C%E6%9C%BA/</link><pubDate>Tue, 14 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/14/dubbo-%E4%BC%98%E9%9B%85%E5%81%9C%E6%9C%BA/</guid><description>&lt;h2 id="背景">背景&lt;/h2>
&lt;p>对于任何一个线上应用，如何在服务更新部署过程中保证客户端无感知是开发者必须要解决的问题，即从应用停止到重启恢复服务这个阶段不能影响正常的业务请求。理想条件下，在没有请求的时候再进行更新是最安全可靠的，然而互联网应用必须要保证可用性，因此在技术层面上优化应用更新流程来保证服务在更新时无损是必要的。&lt;/p>
&lt;p>传统的解决方式是通过将应用更新流程划分为手工摘流量、停应用、更新重启三个步骤，由人工操作实现客户端无对更新感知。这种方式简单而有效，但是限制较多：不仅需要使用借助网关的支持来摘流量，还需要在停应用前人工判断来保证在途请求已经处理完毕。这种需要人工介入的方式运维复杂度较高，只能适用规模较小的应用，无法在大规模系统上使用。&lt;/p>
&lt;p>因此，如果在容器/框架级别提供某种自动化机制，来自动进行摘流量并确保处理完以到达的请求，不仅能保证业务不受更新影响，还可以极大地提升更新应用时的运维效率。&lt;/p>
&lt;p>这个机制也就是优雅停机，目前Tomcat/Undertow/Dubbo等容器/框架都有提供相关实现。下面给出正式一些的定义：优雅停机是指在停止应用时，执行的一系列保证应用正常关闭的操作。这些操作往往包括等待已有请求执行完成、关闭线程、关闭连接和释放资源等，优雅停机可以避免非正常关闭程序可能造成数据异常或丢失，应用异常等问题。优雅停机本质上是JVM即将关闭前执行的一些额外的处理代码。&lt;/p>
&lt;h2 id="适用场景">适用场景&lt;/h2>
&lt;ul>
&lt;li>JVM主动关闭(&lt;code>System.exit(int)&lt;/code>；&lt;/li>
&lt;li>JVM由于资源问题退出(&lt;code>OOM&lt;/code>)；&lt;/li>
&lt;li>应用程序接受到&lt;code>SIGTERM&lt;/code>或&lt;code>SIGINT&lt;/code>信号。&lt;/li>
&lt;/ul>
&lt;h2 id="配置方式">配置方式&lt;/h2>
&lt;h3 id="服务的优雅停机">服务的优雅停机&lt;/h3>
&lt;p>在Dubbo中，优雅停机是默认开启的，停机等待时间为10000毫秒。可以通过配置&lt;code>dubbo.service.shutdown.wait&lt;/code>来修改等待时间。&lt;/p>
&lt;p>例如将等待时间设置为20秒可通过增加以下配置实现：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>dubbo.service.shutdown.wait&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">20000&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="容器的优雅停机">容器的优雅停机&lt;/h3>
&lt;p>当使用&lt;code>org.apache.dubbo.container.Main&lt;/code>这种容器方式来使用 Dubbo 时，也可以通过配置&lt;code>dubbo.shutdown.hook&lt;/code>为&lt;code>true&lt;/code>来开启优雅停机。&lt;/p>
&lt;h3 id="通过qos优雅上下线">通过QOS优雅上下线&lt;/h3>
&lt;p>基于&lt;code>ShutdownHook&lt;/code>方式的优雅停机无法确保所有关闭流程一定执行完，所以 Dubbo 推出了多段关闭的方式来保证服务完全无损。&lt;/p>
&lt;p>多段关闭即将停止应用分为多个步骤，通过运维自动化脚本或手工操作的方式来保证脚本每一阶段都能执行完毕。&lt;/p>
&lt;p>在关闭应用前，首先通过 QOS 的&lt;code>offline&lt;/code>指令下线所有服务，然后等待一定时间确保已经到达请求全部处理完毕，由于服务已经在注册中心下线，当前应用不会有新的请求。这时再执行真正的关闭(&lt;code>SIGTERM&lt;/code> 或&lt;code> SIGINT&lt;/code>)流程，就能保证服务无损。&lt;/p>
&lt;p>QOS可通过 telnet 或 HTTP 方式使用，具体方式请见&lt;a href="https://deploy-preview-3202--dubbo.netlify.app/zh-cn/docsv2.7/user/references/qos/">Dubbo-QOS命令使用说明&lt;/a>。&lt;/p>
&lt;h2 id="流程">流程&lt;/h2>
&lt;p>Provider在接收到停机指令后&lt;/p>
&lt;ul>
&lt;li>从注册中心上注销所有服务；&lt;/li>
&lt;li>从配置中心取消监听动态配置；&lt;/li>
&lt;li>向所有连接的客户端发送只读事件，停止接收新请求；&lt;/li>
&lt;li>等待一段时间以处理已到达的请求，然后关闭请求处理线程池；&lt;/li>
&lt;li>断开所有客户端连接。&lt;/li>
&lt;/ul>
&lt;p>Consumer在接收到停机指令后&lt;/p>
&lt;ul>
&lt;li>拒绝新到请求，直接返回调用异常；&lt;/li>
&lt;li>等待当前已发送请求执行完毕，如果响应超时则强制关闭连接。&lt;/li>
&lt;/ul>
&lt;p>当使用容器方式运行 Dubbo 时，在容器准备退出前，可进行一系列的资源释放和清理工。&lt;/p>
&lt;p>例如使用 SpringContainer时，Dubbo 的ShutdownHook线程会执行&lt;code>ApplicationContext&lt;/code>的&lt;code>stop&lt;/code>和&lt;code>close&lt;/code>方法，保证 Bean的生命周期完整。&lt;/p>
&lt;h2 id="实现原理">实现原理&lt;/h2>
&lt;ol>
&lt;li>
&lt;p>在加载类&lt;code>org.apache.dubbo.config.AbstractConfig&lt;/code>时，通过&lt;code>org.apache.dubbo.config.DubboShutdownHook&lt;/code>向JVM注册 ShutdownHook。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Register the ShutdownHook
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">register&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (&lt;span style="color:#719e07">!&lt;/span>registered.get() &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> registered.compareAndSet(&lt;span style="color:#cb4b16">false&lt;/span>, &lt;span style="color:#cb4b16">true&lt;/span>)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Runtime.getRuntime().addShutdownHook(getDubboShutdownHook());
&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;/li>
&lt;li>
&lt;p>每个ShutdownHook都是一个单独的线程，由JVM在退出时触发执行&lt;code>org.apache.dubbo.config.DubboShutdownHook&lt;/code>。&lt;/p></description></item><item><title>Dubbo的泛化调用</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/14/dubbo%E7%9A%84%E6%B3%9B%E5%8C%96%E8%B0%83%E7%94%A8/</link><pubDate>Tue, 14 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/14/dubbo%E7%9A%84%E6%B3%9B%E5%8C%96%E8%B0%83%E7%94%A8/</guid><description>&lt;p>以下几种场景可以考虑使用泛化调用：&lt;/p>
&lt;ul>
&lt;li>服务测试平台&lt;/li>
&lt;li>API 服务网关&lt;/li>
&lt;/ul>
&lt;p>泛化调用主要用于消费端没有 API 接口的情况；不需要引入接口 jar 包，而是直接通过 GenericService 接口来发起服务调用，参数及返回值中的所有 POJO 均用 &lt;code>Map&lt;/code> 表示。泛化调用对于服务端无需关注，按正常服务进行暴露即可。&lt;/p>
&lt;p>下面来看看消费端如何使用泛化调用进行服务调用。&lt;/p>
&lt;h2 id="通过-spring-xml-配置进行泛化调用">通过 Spring XML 配置进行泛化调用&lt;/h2>
&lt;p>在 Spring 配置申明 &lt;code>generic=&amp;quot;true&amp;quot;&lt;/code>，如：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;userService&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.samples.generic.api.IUserService&amp;#34;&lt;/span> generic=&lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>需要使用的地方，通过强制类型转化为 GenericService 进行调用：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>GenericService userService &lt;span style="color:#719e07">=&lt;/span> (GenericService) context.getBean(&lt;span style="color:#2aa198">&amp;#34;userService&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// primary param and return value&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>String name &lt;span style="color:#719e07">=&lt;/span> (String) userService.$invoke(&lt;span style="color:#2aa198">&amp;#34;delete&amp;#34;&lt;/span>, &lt;span style="color:#719e07">new&lt;/span> String&lt;span style="color:#719e07">[]&lt;/span>{&lt;span style="color:#dc322f">int&lt;/span>.class.getName()}, &lt;span style="color:#719e07">new&lt;/span> Object&lt;span style="color:#719e07">[]&lt;/span>{1});
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>System.out.println(name);
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>其中：&lt;/p>
&lt;ol>
&lt;li>GenericService 这个接口只有一个方法，名为 &lt;code>$invoke&lt;/code>，它接受三个参数，分别为方法名、方法参数类型数组和参数值数组；&lt;/li>
&lt;li>对于方法参数类型数组
&lt;ol>
&lt;li>如果是基本类型，如 int 或 long，可以使用 &lt;code>int.class.getName()&lt;/code>获取其类型；&lt;/li>
&lt;li>如果是基本类型数组，如 int[]，则可以使用 &lt;code>int[].class.getName()&lt;/code>；&lt;/li>
&lt;li>如果是 POJO，则直接使用全类名，如 &lt;code>com.alibaba.dubbo.samples.generic.api.Params&lt;/code>。&lt;/li>
&lt;/ol>
&lt;/li>
&lt;/ol>
&lt;h2 id="通过-api-编程进行泛化调用">通过 API 编程进行泛化调用&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>ApplicationConfig application = new ApplicationConfig();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>application.setName(&amp;#34;api-generic-consumer&amp;#34;);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>RegistryConfig registry = new RegistryConfig();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>registry.setAddress(&amp;#34;zookeeper://127.0.0.1:2181&amp;#34;);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>application.setRegistry(registry);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ReferenceConfig&amp;lt;GenericService&amp;gt; reference = new ReferenceConfig&amp;lt;GenericService&amp;gt;();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>// 弱类型接口名
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>reference.setInterface(&amp;#34;com.alibaba.dubbo.samples.generic.api.IUserService&amp;#34;);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>// 声明为泛化接口
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>reference.setGeneric(true);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>reference.setApplication(application);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>// 用com.alibaba.dubbo.rpc.service.GenericService可以替代所有接口引用
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>GenericService genericService = reference.get();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>String name = (String) genericService.$invoke(&amp;#34;delete&amp;#34;, new String[]{int.class.getName()}, new Object[]{1});
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>System.out.println(name);
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>通过 API 的方式，不需要像 XML 的方式需要提前将服务配置好，可以动态构建 ReferenceConfig；相对 XML 来说，API 的方式更常见。&lt;/p></description></item><item><title>Spring Boot Dubbo应用启停源码分析</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/14/spring-boot-dubbo%E5%BA%94%E7%94%A8%E5%90%AF%E5%81%9C%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/</link><pubDate>Tue, 14 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/14/spring-boot-dubbo%E5%BA%94%E7%94%A8%E5%90%AF%E5%81%9C%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/</guid><description>&lt;h2 id="背景介绍">背景介绍&lt;/h2>
&lt;p>&lt;a href="https://github.com/apache/dubbo-spring-boot-project">Dubbo Spring Boot&lt;/a> 工程致力于简化 Dubbo RPC 框架在Spring Boot应用场景的开发。同时也整合了 Spring Boot 特性：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/apache/dubbo-spring-boot-project/blob/master/dubbo-spring-boot-autoconfigure">自动装配&lt;/a> (比如： 注解驱动, 自动装配等).&lt;/li>
&lt;li>&lt;a href="https://github.com/apache/dubbo-spring-boot-project/blob/master/dubbo-spring-boot-actuator">Production-Ready&lt;/a> (比如： 安全, 健康检查, 外部化配置等).&lt;/li>
&lt;/ul>
&lt;h2 id="dubboconsumer启动分析">DubboConsumer启动分析&lt;/h2>
&lt;p>你有没有想过一个问题？&lt;code>dubbo-spring-boot-project&lt;/code>中的&lt;code>DubboConsumerDemo&lt;/code>应用就一行代码，&lt;code>main&lt;/code>方法执行完之后，为什么不会直接退出呢？&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@SpringBootApplication&lt;/span>(scanBasePackages &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;com.alibaba.boot.dubbo.demo.consumer.controller&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DubboConsumerDemo&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>(String&lt;span style="color:#719e07">[]&lt;/span> args) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> SpringApplication.run(DubboConsumerDemo.class,args);
&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>其实要回答这样一个问题，我们首先需要把这个问题进行一个抽象，即一个JVM进程，在什么情况下会退出？&lt;/p>
&lt;p>以Java 8为例，通过查阅JVM语言规范[1]，在12.8章节中有清晰的描述：&lt;/p>
&lt;p>A program terminates all its activity and &lt;em>exits&lt;/em> when one of two things happens:&lt;/p>
&lt;ul>
&lt;li>All the threads that are not daemon threads terminate.&lt;/li>
&lt;li>Some thread invokes the &lt;code>exit&lt;/code> method of class &lt;code>Runtime&lt;/code> or class &lt;code>System&lt;/code>, and the &lt;code>exit&lt;/code> operation is not forbidden by the security manager.&lt;/li>
&lt;/ul>
&lt;p>也就是说，导致JVM的退出只有2种情况：&lt;/p></description></item><item><title>通过QoS对服务进行动态控制</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/14/%E9%80%9A%E8%BF%87qos%E5%AF%B9%E6%9C%8D%E5%8A%A1%E8%BF%9B%E8%A1%8C%E5%8A%A8%E6%80%81%E6%8E%A7%E5%88%B6/</link><pubDate>Tue, 14 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/14/%E9%80%9A%E8%BF%87qos%E5%AF%B9%E6%9C%8D%E5%8A%A1%E8%BF%9B%E8%A1%8C%E5%8A%A8%E6%80%81%E6%8E%A7%E5%88%B6/</guid><description>&lt;p>QoS，全称为&lt;code>Quality of Service&lt;/code>, 是常见于网络设备中的一个术语 ，例如在路由器中，可以通过Qos动态的调整和控制某些端口的权重，从而优先的保障运行在这些端口上的服务质量。&lt;/p>
&lt;p>在Dubbo中，QoS这个概念被用于动态的对服务进行查询和控制。例如对获取当前提供和消费的所有服务，以及对服务进行动态的上下线，即从注册中心上进行注册和反注册操作。&lt;/p>
&lt;h2 id="qos工作机制">QoS工作机制&lt;/h2>
&lt;p>从Dubbo 2.5.8开始，默认引入了Qos功能，默认处于启动状态。所有的QoS功能被抽象成一个个的命令，通过执行这些命令，Qos会返回响应的结果。&lt;/p>
&lt;blockquote>
&lt;p>Qos功能基于Netty4实现，在Dubbo 2.6.x之前的版本中，默认依赖的是Netty3，因此需要显示的添加Netty4的依赖，才能确保Netty4正常工作。如果使用http://start.dubbo.io自动生成的Dubbo应用，则无需添加额外的配置，因为已经默认加上了Netty4的依赖。&lt;/p>
&lt;/blockquote>
&lt;p>Qos的工作机制如下图所示：&lt;/p>
&lt;p>&lt;img alt="undefined" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/qos-architecture.png">&lt;/p>
&lt;ol>
&lt;li>启动并监听一个端口，默认端口是22222&lt;/li>
&lt;li>识别目标请求的协议是Http或者是Telnet，根据协议不同动态添加对应的处理器&lt;/li>
&lt;li>针对不同的协议进行解码，解析出需要执行的命令&lt;/li>
&lt;li>执行命令并返回结果&lt;/li>
&lt;/ol>
&lt;h2 id="qos命令">QoS命令&lt;/h2>
&lt;p>QoS目前支持的命令包括：&lt;/p>
&lt;ul>
&lt;li>help: 帮助命令，列出&lt;/li>
&lt;li>ls: 列出当前所有的正在提供的服务，以及消费的服务&lt;/li>
&lt;li>online: 动态将某个或全部服务向注册中心进行注册&lt;/li>
&lt;li>offline: 动态将某个或全部服务从注册中心摘除（反注册）&lt;/li>
&lt;li>quit: 退出当前telnet会话&lt;/li>
&lt;/ul>
&lt;p>下面，我们具体来操作一下如何通过用QoS对服务进行动态控制。&lt;/p>
&lt;h3 id="通过telnet方式访问qos">通过Telnet方式访问QoS&lt;/h3>
&lt;p>假设我们的Dubbo服务端已经启动，我们通过Telnet方式进行连接：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>$ telnet localhost 22222
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Trying 127.0.0.1...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Connected to localhost.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Escape character is &amp;#39;^]&amp;#39;.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ????????? ??? ?? ??????????? ??????????? ????????
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ??? ???? ??? ??? ??? ??? ??? ??? ??? ???
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ??? ??? ??? ??? ??? ??? ??? ??? ??? ???
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ??? ??? ??? ??? ?????????? ?????????? ??? ???
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ??? ??? ??? ??? ??????????? ??????????? ??? ???
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ??? ??? ??? ??? ??? ??? ??? ??? ??? ???
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ??? ???? ??? ??? ??? ??? ??? ??? ??? ???
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ????????? ????????? ??????????? ??????????? ????????
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>连接成功后，会出现&lt;code>dubbo&amp;gt;&lt;/code>提示符，此时输入&lt;code>help&lt;/code>命令&lt;/p></description></item><item><title>Dubbo的负载均衡</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/10/dubbo%E7%9A%84%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/</link><pubDate>Fri, 10 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/10/dubbo%E7%9A%84%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/</guid><description>&lt;h2 id="背景">背景&lt;/h2>
&lt;p>Dubbo是一个分布式服务框架，能避免单点故障和支持服务的横向扩容。一个服务通常会部署多个实例。如何从多个服务 Provider 组成的集群中挑选出一个进行调用，就涉及到一个负载均衡的策略。&lt;/p>
&lt;h2 id="几个概念">几个概念&lt;/h2>
&lt;p>在讨论负载均衡之前，我想先解释一下这3个概念。&lt;/p>
&lt;ol>
&lt;li>负载均衡&lt;/li>
&lt;li>集群容错&lt;/li>
&lt;li>服务路由&lt;/li>
&lt;/ol>
&lt;p>这3个概念容易混淆。他们都描述了怎么从多个 Provider 中选择一个来进行调用。那他们到底有什么区别呢?下面我来举一个简单的例子，把这几个概念阐述清楚吧。&lt;/p>
&lt;p>有一个Dubbo的用户服务，在北京部署了10个，在上海部署了20个。一个杭州的服务消费方发起了一次调用，然后发生了以下的事情:&lt;/p>
&lt;ol>
&lt;li>根据配置的路由规则，如果杭州发起的调用，会路由到比较近的上海的20个 Provider。&lt;/li>
&lt;li>根据配置的随机负载均衡策略，在20个 Provider 中随机选择了一个来调用，假设随机到了第7个 Provider。&lt;/li>
&lt;li>结果调用第7个 Provider 失败了。&lt;/li>
&lt;li>根据配置的Failover集群容错模式，重试其他服务器。&lt;/li>
&lt;li>重试了第13个 Provider，调用成功。&lt;/li>
&lt;/ol>
&lt;p>上面的第1，2，4步骤就分别对应了路由，负载均衡和集群容错。 Dubbo中，先通过路由，从多个 Provider 中按照路由规则，选出一个子集。再根据负载均衡从子集中选出一个 Provider 进行本次调用。如果调用失败了，根据集群容错策略，进行重试或定时重发或快速失败等。 可以看到Dubbo中的路由，负载均衡和集群容错发生在一次RPC调用的不同阶段。最先是路由，然后是负载均衡，最后是集群容错。 本文档只讨论负载均衡，路由和集群容错在其他的文档中进行说明。&lt;/p>
&lt;h2 id="dubbo内置负载均衡策略">Dubbo内置负载均衡策略&lt;/h2>
&lt;p>Dubbo内置了4种负载均衡策略:&lt;/p>
&lt;ol>
&lt;li>RandomLoadBalance:随机负载均衡。随机的选择一个。是Dubbo的&lt;strong>默认&lt;/strong>负载均衡策略。&lt;/li>
&lt;li>RoundRobinLoadBalance:轮询负载均衡。轮询选择一个。&lt;/li>
&lt;li>LeastActiveLoadBalance:最少活跃调用数，相同活跃数的随机。活跃数指调用前后计数差。使慢的 Provider 收到更少请求，因为越慢的 Provider 的调用前后计数差会越大。&lt;/li>
&lt;li>ConsistentHashLoadBalance:一致性哈希负载均衡。相同参数的请求总是落在同一台机器上。&lt;/li>
&lt;/ol>
&lt;h3 id="1随机负载均衡">1.随机负载均衡&lt;/h3>
&lt;p>顾名思义，随机负载均衡策略就是从多个 Provider 中随机选择一个。但是 Dubbo 中的随机负载均衡有一个权重的概念，即按照权重设置随机概率。比如说，有10个 Provider，并不是说，每个 Provider 的概率都是一样的，而是要结合这10个 Provider 的权重来分配概率。&lt;/p>
&lt;p>Dubbo中，可以对 Provider 设置权重。比如机器性能好的，可以设置大一点的权重，性能差的，可以设置小一点的权重。权重会对负载均衡产生影响。可以在Dubbo Admin中对 Provider 进行权重的设置。&lt;/p>
&lt;p>&lt;strong>基于权重的负载均衡算法&lt;/strong>&lt;/p>
&lt;p>随机策略会先判断所有的 Invoker 的权重是不是一样的，如果都是一样的，那么处理就比较简单了。使用random.nexInt(length)就可以随机生成一个 Invoker 的序号,根据序号选择对应的 Invoker 。如果没有在Dubbo Admin中对服务 Provider 设置权重，那么所有的 Invoker 的权重就是一样的，默认是100。 如果权重不一样，那就需要结合权重来设置随机概率了。算法大概如下： 假如有4个 Invoker。&lt;/p></description></item><item><title>Dubbo 注解驱动</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/07/dubbo-%E6%B3%A8%E8%A7%A3%E9%A9%B1%E5%8A%A8/</link><pubDate>Tue, 07 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/07/dubbo-%E6%B3%A8%E8%A7%A3%E9%A9%B1%E5%8A%A8/</guid><description>&lt;h2 id="注解驱动annotation-driven">注解驱动（Annotation-Driven）&lt;/h2>
&lt;h3 id="dubbocomponentscan">&lt;code>@DubboComponentScan&lt;/code>&lt;/h3>
&lt;h4 id="起始版本-257">起始版本： &lt;code>2.5.7&lt;/code>&lt;/h4>
&lt;h4 id="dubboannotation-历史遗留问题">&lt;code>&amp;lt;dubbo:annotation&amp;gt; &lt;/code>历史遗留问题&lt;/h4>
&lt;h5 id="1-注解支持不充分">1. 注解支持不充分&lt;/h5>
&lt;p>在 Dubbo &lt;code>2.5.7&lt;/code>之前的版本 ，Dubbo 提供了两个核心注解 &lt;code>@Service&lt;/code> 以及 &lt;code>@Reference&lt;/code>，分别用于Dubbo 服务提供和 Dubbo 服务引用。&lt;/p>
&lt;p>其中，&lt;code>@Service&lt;/code> 作为 XML 元素 &lt;code>&amp;lt;dubbo:service&amp;gt;&lt;/code>的替代注解，与 Spring Framework &lt;code>@org.springframework.stereotype.Service&lt;/code> 类似，用于服务提供方 Dubbo 服务暴露。与之相对应的&lt;code>@Reference&lt;/code>，则是替代&lt;code>&amp;lt;dubbo:reference&lt;/code> 元素，类似于 Spring 中的 &lt;code>@Autowired&lt;/code>。
&lt;code>2.5.7&lt;/code> 之前的Dubbo，与早期的 Spring Framework 2.5 存在类似的不足，即注解支持不够充分。注解需要和 XML 配置文件配合使用，如下所示：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;beans&lt;/span> xmlns:xsi=&lt;span style="color:#2aa198">&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> xmlns:dubbo=&lt;span style="color:#2aa198">&amp;#34;http://code.alibabatech.com/schema/dubbo&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> xmlns=&lt;span style="color:#2aa198">&amp;#34;http://www.springframework.org/schema/beans&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> xsi:schemaLocation=&lt;span style="color:#2aa198">&amp;#34;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">	http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd&amp;#34;&lt;/span>&lt;span style="color:#268bd2">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo:application&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;annotation-provider&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo:registry&lt;/span> address=&lt;span style="color:#2aa198">&amp;#34;127.0.0.1:4548&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo:annotation&lt;/span> package=&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.config.spring.annotation.provider&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/beans&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="2--service-bean-不支持-spring-aop">2. &lt;code>@Service&lt;/code> Bean 不支持 Spring AOP&lt;/h5>
&lt;p>同时，使用 &lt;code>&amp;lt;dubbo:annotation&amp;gt; &lt;/code> 方式扫描后的Dubbo &lt;code>@Service&lt;/code> ，在 Spring 代理方面存在问题，如 GitHub 上的 &lt;a href="https://github.com/apache/dubbo/issues/794">issue&lt;/a>：&lt;/p></description></item><item><title>第一个 Dubbo 应用</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/07/dubbo-101/</link><pubDate>Tue, 07 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/07/dubbo-101/</guid><description>&lt;h2 id="java-rmi-简介">Java RMI 简介&lt;/h2>
&lt;p>Java RMI （Remote Method Invocation）- 远程方法调用，能够让客户端像使用本地调用一样调用服务端 Java 虚拟机中的对象方法。RMI 是面向对象语言领域对 RPC （Remote Procedure Call）的完善，用户无需依靠 IDL 的帮助来完成分布式调用，而是通过依赖接口这种更简单自然的方式。&lt;/p>
&lt;h3 id="java-rmi-工作原理">Java RMI 工作原理&lt;/h3>
&lt;p>一个典型的 RMI 调用如下图所示：&lt;/p>
&lt;ol>
&lt;li>服务端向 RMI 注册服务绑定自己的地址，&lt;/li>
&lt;li>客户端通过 RMI 注册服务获取目标地址，&lt;/li>
&lt;li>客户端调用本地的 Stub 对象上的方法，和调用本地对象上的方法一致，&lt;/li>
&lt;li>本地存根对象将调用信息打包，通过网络发送到服务端，&lt;/li>
&lt;li>服务端的 Skeleton 对象收到网络请求之后，将调用信息解包，&lt;/li>
&lt;li>然后找到真正的服务对象发起调用，并将返回结果打包通过网络发送回客户端。&lt;/li>
&lt;/ol>
&lt;p>&lt;img alt="RMI Flow" src="https://deploy-preview-3202--dubbo.netlify.app/imgs/blog/rmi-flow.png">&lt;/p>
&lt;p>(来源：https://www.cs.rutgers.edu/~pxk/417/notes/images/rpc-rmi_flow.png)&lt;/p>
&lt;h3 id="java-rmi-基本概念">Java RMI 基本概念&lt;/h3>
&lt;p>Java RMI 是 Java 领域创建分布式应用的技术基石。后续的 EJB 技术，以及现代的分布式服务框架，其中的基本理念依旧是 Java RMI 的延续。在 RMI 调用中，有以下几个核心的概念：&lt;/p>
&lt;ol>
&lt;li>
&lt;p>通过&lt;strong>接口&lt;/strong>进行远程调用&lt;/p>
&lt;/li>
&lt;li>
&lt;p>通过客户端的 &lt;strong>Stub 对象&lt;/strong>和服务端的 &lt;strong>Skeleton 对象&lt;/strong>的帮助将远程调用伪装成本地调用&lt;/p>
&lt;/li>
&lt;li>
&lt;p>通过 &lt;strong>RMI 注册服务&lt;/strong>完成服务的注册和发现&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>对于第一点，客户端需要依赖接口，而服务端需要提供该接口的实现。&lt;/p>
&lt;p>对于第二点，在 J2SE 1.5 版本之前需要通过 rmic 预先编译好客户端的 Stub 对象和服务端的 Skeleton 对象。在之后的版本中，不再需要事先生成 Stub 和 Skeleton 对象。&lt;/p></description></item><item><title>使用jdk17编译运行dubbo 2.7.14项目</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/07/%E4%BD%BF%E7%94%A8jdk17%E7%BC%96%E8%AF%91%E8%BF%90%E8%A1%8Cdubbo-2.7.14%E9%A1%B9%E7%9B%AE/</link><pubDate>Tue, 07 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/07/%E4%BD%BF%E7%94%A8jdk17%E7%BC%96%E8%AF%91%E8%BF%90%E8%A1%8Cdubbo-2.7.14%E9%A1%B9%E7%9B%AE/</guid><description>&lt;h2 id="概述">概述&lt;/h2>
&lt;p>java 17是java目前最新的长期支持(LTS)版本，但是由于其强封装 JDK 的内部 API的新特性，导致dubbo项目无法直接使用jdk17编译运行。通过参考&lt;a href="https://openjdk.java.net/jeps/403">openjdk的说明&lt;/a>，可以发现只需要添加相应参数即可绕开java 17的限制&lt;br>
对于普通的dubbo项目，只需要在运行时添加&lt;br>
&lt;code>--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/sun.reflect.generics.reflectiveObjects=ALL-UNNAMED --add-opens java.base/java.math=ALL-UNNAMED&lt;/code>&lt;br>
如上参数即可。如果项目的其它依赖也有类似问题则可能需要加入更多参数，参数的获得方式和详细示例将在下面给出&lt;br>
本解决方案只能解决由于java 17强封装 JDK 的内部 API的特性造成的问题，其他的兼容性问题请寻找其它方案&lt;/p>
&lt;h2 id="参数的获得方法和示例">参数的获得方法和示例&lt;/h2>
&lt;p>我们以dubbo官方仓库中的demo为例
首先使用java 17作为我们的开发环境，通过&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>git clone git@github.com:apache/dubbo.git
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>git checkout dubbo-2.7.14
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cd dubbo-demo/dubbo-demo-annotation
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>获得dubbo官方仓库的代码中的demo，然后可以尝试直接使用java 17编译dubbo的demo&lt;br>
确认java版本&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>➜ ~ java -version
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>openjdk version &amp;#34;17.0.1&amp;#34; 2021-10-19
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>OpenJDK Runtime Environment Temurin-17.0.1+12 (build 17.0.1+12)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>OpenJDK 64-Bit Server VM Temurin-17.0.1+12 (build 17.0.1+12, mixed mode, sharing)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>然后运行&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>mvn &lt;span style="color:#719e07">-&lt;/span>U clean &lt;span style="color:#719e07">package&lt;/span> &lt;span style="color:#719e07">--&lt;/span>no&lt;span style="color:#719e07">-&lt;/span>transfer&lt;span style="color:#719e07">-&lt;/span>progress &lt;span style="color:#719e07">-&lt;/span>D maven.test.skip=&lt;span style="color:#cb4b16">true&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>启动zookeeper &lt;code>docker run --name some-zookeepep -p 2181:2181 -it --rm zookeeper&lt;/code> 作为注册中心
尝试运行provider&lt;/p></description></item><item><title>在 Dubbo 中使用注解</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/07/%E5%9C%A8-dubbo-%E4%B8%AD%E4%BD%BF%E7%94%A8%E6%B3%A8%E8%A7%A3/</link><pubDate>Tue, 07 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/07/%E5%9C%A8-dubbo-%E4%B8%AD%E4%BD%BF%E7%94%A8%E6%B3%A8%E8%A7%A3/</guid><description>&lt;h1 id="在-dubbo-中使用注解">在 Dubbo 中使用注解&lt;/h1>
&lt;p>随着微服务架构的广泛地推广和实施。在 Java 生态系统中，以 Spring Boot 和 Spring Cloud 为代表的微服务框架，引入了全新的编程模型，包括：&lt;/p>
&lt;ul>
&lt;li>注解驱动（Annotation-Driven）&lt;/li>
&lt;li>外部化配置（External Configuration）&lt;/li>
&lt;li>以及自动装配（Auto-Configure）&lt;/li>
&lt;/ul>
&lt;p>新的编程模型无需 XML 配置、简化部署、提升开发效率。为了更好地实践微服务架构，Dubbo 从 &lt;code>2.5.8&lt;/code> 版本开始， 分别针对了上述的三个场景，提供了更完善的支持。本文不讨论传统的 XML 配置方式，而是侧重介绍注解这种方式。外部配置、自动装配两种自动装配会在另外的文章中专门介绍。&lt;/p>
&lt;h2 id="注解介绍">注解介绍&lt;/h2>
&lt;h3 id="enabledubbo">@EnableDubbo&lt;/h3>
&lt;p>&lt;code>@EnableDubbo&lt;/code> 注解是 &lt;code>@EnableDubboConfig&lt;/code> 和 &lt;code>@DubboComponentScan&lt;/code>两者组合的便捷表达方式。与注解驱动相关的是 &lt;code>@DubboComponentScan&lt;/code>。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> org.apache.dubbo.config.spring.context.annotation;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@EnableDubboConfig&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@DubboComponentScan&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">@interface&lt;/span> EnableDubbo {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Base packages to scan for annotated @Service classes.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * &amp;lt;p&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Use {@link #scanBasePackageClasses()} for a type-safe alternative to String-based
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * package names.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @return the base packages to scan
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @see DubboComponentScan#basePackages()
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@AliasFor&lt;/span>(annotation &lt;span style="color:#719e07">=&lt;/span> DubboComponentScan.class, attribute &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;basePackages&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String&lt;span style="color:#719e07">[]&lt;/span> &lt;span style="color:#268bd2">scanBasePackages&lt;/span>() &lt;span style="color:#719e07">default&lt;/span> {};
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Type-safe alternative to {@link #scanBasePackages()} for specifying the packages to
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * scan for annotated @Service classes. The package of each class specified will be
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * scanned.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @return classes from the base packages to scan
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @see DubboComponentScan#basePackageClasses
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@AliasFor&lt;/span>(annotation &lt;span style="color:#719e07">=&lt;/span> DubboComponentScan.class, attribute &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;basePackageClasses&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;[]&lt;/span> scanBasePackageClasses() &lt;span style="color:#719e07">default&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>@EnableDubbo&lt;/code> 可以在指定的包名下（通过 &lt;code>scanBasePackages&lt;/code>），或者指定的类中（通过 &lt;code>scanBasePackageClasses&lt;/code>）扫描 Dubbo 的服务提供者（以 &lt;code>@Service&lt;/code> 标注）以及 Dubbo 的服务消费者（以 &lt;code>Reference&lt;/code> 标注）。&lt;/p></description></item><item><title>Dubbo 2.7.x repackage 后的兼容实现方案</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/07/22/dubbo-2.7.x-repackage-%E5%90%8E%E7%9A%84%E5%85%BC%E5%AE%B9%E5%AE%9E%E7%8E%B0%E6%96%B9%E6%A1%88/</link><pubDate>Sun, 22 Jul 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/07/22/dubbo-2.7.x-repackage-%E5%90%8E%E7%9A%84%E5%85%BC%E5%AE%B9%E5%AE%9E%E7%8E%B0%E6%96%B9%E6%A1%88/</guid><description>&lt;p>Dubbo至加入Apache孵化器以来，一个很强的诉求就是需要rename groupId和package name，这两项工作在项目毕业前需要完成。其中rename package相对来说复杂一些，除了要修改所有类的包名为&lt;code>org.apache.dubbo&lt;/code>外，更多的是需要考虑如何老版本的兼容性。&lt;/p>
&lt;p>常见的兼容性包括但不限于以下几种情况：&lt;/p>
&lt;ul>
&lt;li>用户API
&lt;ul>
&lt;li>编程API&lt;/li>
&lt;li>Spring注解&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>扩展SPI
&lt;ul>
&lt;li>扩展Filter&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>2.7.x里就是通过增加了一个新的模块&lt;code>dubbo-compatible&lt;/code>来解决以上兼容性问题。&lt;/p>
&lt;h2 id="编程使用api">编程使用API&lt;/h2>
&lt;p>编程使用API是最直接最原始的使用方式，其他方式诸如Spring schema、注解等方式都是基于原始API的；因此非常有必要对API编程形式进行兼容。&lt;/p>
&lt;p>所有编程相关API的兼容代码均在&lt;code>com.alibaba.dubbo.config&lt;/code>包下，下面我们看看几个常见API的兼容实现。&lt;/p>
&lt;h3 id="applicationconfig">ApplicationConfig&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> com.alibaba.dubbo.config;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Deprecated&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ApplicationConfig&lt;/span> &lt;span style="color:#268bd2">extends&lt;/span> org.apache.dubbo.config.ApplicationConfig {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">ApplicationConfig&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>();
&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">ApplicationConfig&lt;/span>(String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>(name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="protocolconfig">ProtocolConfig&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> com.alibaba.dubbo.config;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Deprecated&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ProtocolConfig&lt;/span> &lt;span style="color:#268bd2">extends&lt;/span> org.apache.dubbo.config.ProtocolConfig {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">ProtocolConfig&lt;/span>() {
&lt;/span>&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">ProtocolConfig&lt;/span>(String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>(name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">ProtocolConfig&lt;/span>(String name, &lt;span style="color:#dc322f">int&lt;/span> port) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>(name, port);
&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;/p>
&lt;ol>
&lt;li>兼容类是直接通过继续repacakge后的类，达到最大程度的代码复用；&lt;/li>
&lt;li>构造函数也需要保持兼容；&lt;/li>
&lt;/ol>
&lt;p>整个兼容包中，除了上述API以外，包括一些常用的类比如&lt;code>Constants&lt;/code>、&lt;code>URL&lt;/code>以及绝大部分的兼容类都是通过简单的继承，让用户基于老的API实现的类能正确运行。&lt;/p></description></item><item><title>Dubbo 上下文信息</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/07/12/dubbo-%E4%B8%8A%E4%B8%8B%E6%96%87%E4%BF%A1%E6%81%AF/</link><pubDate>Thu, 12 Jul 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/07/12/dubbo-%E4%B8%8A%E4%B8%8B%E6%96%87%E4%BF%A1%E6%81%AF/</guid><description>&lt;h2 id="简介">简介&lt;/h2>
&lt;p>上下文信息是一次 RPC 调用过程中附带的环境信息，如方法名、参数类型、真实参数、本端/对端地址等。这些数据仅属于一次调用，作用于 Consumer 到 Provider 调用的整个流程。&lt;/p>
&lt;p>提供上下文信息是 RPC 框架很重要的一个功能，使用上下文不仅可以为单次调用指定不同配置，还能在此基础上提供强大的上层功能，如分布式链路追踪。其实现原理就是在上下文中维护一个&lt;code>span_id&lt;/code>，Consumer 和 Provider 通过传递&lt;code>span_id&lt;/code>来连接一次RPC调用，分别上报日志后可以在追踪系统中串联并展示完整的调用流程。这样可以更方便地发现异常，定位问题。&lt;/p>
&lt;h2 id="使用说明">使用说明&lt;/h2>
&lt;p>Dubbo中代表上下文的类是&lt;code>org.apache.dubbo.rpc.RpcContext&lt;/code>，可通过下述代码来获取上下文信息。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>RpcContext.getContext()
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="使用场景">使用场景&lt;/h2>
&lt;h3 id="获取调用信息">获取调用信息&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th style="text-align: left">方法名&lt;/th>
 &lt;th style="text-align: left">用途&lt;/th>
 &lt;th style="text-align: left">作用范围&lt;/th>
 &lt;th style="text-align: left">说明&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td style="text-align: left">getRequest&lt;/td>
 &lt;td style="text-align: left">获取 RPC 请求对象&lt;/td>
 &lt;td style="text-align: left">Consumer&lt;/td>
 &lt;td style="text-align: left">获取底层 RPC 请求对象，例如 HttpServletRequest，其他情况为 null&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">getResponse&lt;/td>
 &lt;td style="text-align: left">获取 RPC 请求响应&lt;/td>
 &lt;td style="text-align: left">Consumer&lt;/td>
 &lt;td style="text-align: left">获取底层 RPC 响应对象，例如HttpServletResponse，其他情况为 null&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">isProviderSide&lt;/td>
 &lt;td style="text-align: left">当前是否属于 Provider 上下文&lt;/td>
 &lt;td style="text-align: left">Both&lt;/td>
 &lt;td style="text-align: left">服务被调用时为 true，调用其他服务时为false&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">isConsumerSide&lt;/td>
 &lt;td style="text-align: left">当前是否属于 Consumer 上下文&lt;/td>
 &lt;td style="text-align: left">Both&lt;/td>
 &lt;td style="text-align: left">服务被调用时为 false，调用其他服务时为 true&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">getUrls&lt;/td>
 &lt;td style="text-align: left">获取当前能调用的 Url 列表&lt;/td>
 &lt;td style="text-align: left">Both&lt;/td>
 &lt;td style="text-align: left">Consumer 端会根据不同的 Failover 策略实时变化&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">getRemotePort&lt;/td>
 &lt;td style="text-align: left">获取远端端口&lt;/td>
 &lt;td style="text-align: left">Both&lt;/td>
 &lt;td style="text-align: left">Consumer 端为最后一次调用的 Provider 端口，Provider 为当前请求的 Consumer 端口&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">getRemoteHost&lt;/td>
 &lt;td style="text-align: left">获取远端主机地址&lt;/td>
 &lt;td style="text-align: left">Both&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">getRemoteHostName&lt;/td>
 &lt;td style="text-align: left">获取远端主机名&lt;/td>
 &lt;td style="text-align: left">Both&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">getRemoteAddressString&lt;/td>
 &lt;td style="text-align: left">获取远端地址&lt;/td>
 &lt;td style="text-align: left">Both&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">getRemoteAddress&lt;/td>
 &lt;td style="text-align: left">获取远端地址&lt;/td>
 &lt;td style="text-align: left">Both&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">getLocalPort&lt;/td>
 &lt;td style="text-align: left">获取本端端口&lt;/td>
 &lt;td style="text-align: left">Both&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">getLocalHost&lt;/td>
 &lt;td style="text-align: left">获取本端主机地址&lt;/td>
 &lt;td style="text-align: left">Both&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">getLocalHostName&lt;/td>
 &lt;td style="text-align: left">获取本端主机名&lt;/td>
 &lt;td style="text-align: left">Both&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">getLocalAddressString&lt;/td>
 &lt;td style="text-align: left">获取本端地址&lt;/td>
 &lt;td style="text-align: left">Both&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">getLocalAddress&lt;/td>
 &lt;td style="text-align: left">获取本端地址&lt;/td>
 &lt;td style="text-align: left">Both&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h3 id="传递用户参数">传递用户参数&lt;/h3>
&lt;h4 id="本端传递">本端传递&lt;/h4>
&lt;p>调用&lt;code>get&lt;/code>和&lt;code>set&lt;/code>方法即可完成参数传递。主要用于本端 Filter 之间的数据共享。&lt;/p></description></item><item><title>第一个 Dubbo Filter</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/07/01/%E7%AC%AC%E4%B8%80%E4%B8%AA-dubbo-filter/</link><pubDate>Sun, 01 Jul 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/07/01/%E7%AC%AC%E4%B8%80%E4%B8%AA-dubbo-filter/</guid><description>&lt;h3 id="概述">概述&lt;/h3>
&lt;p>在Dubbo的整体设计中，Filter是一个很重要的概念，包括Dubbo本身的大多数功能，都是基于此扩展点实现的，在每次的调用过程中，Filter的拦截都会被执行。&lt;/p>
&lt;h4 id="dubbo-filter的加载机制">Dubbo Filter的加载机制&lt;/h4>
&lt;p>Dubbo中已经实现的Filter大概有二十几个，它们的入口都是ProtocolFilterWrapper，ProtocolFilterWrapper对Protocol做了Wrapper，会在加载扩展的时候被加载进来，下面我们来看下这个Filter链是如何构造的。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//ProtocolFilterWrapper.java&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#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:#719e07">if&lt;/span> (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&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 style="color:#719e07">return&lt;/span> buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);
&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">static&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">buildInvokerChain&lt;/span>(&lt;span style="color:#268bd2">final&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> invoker, String key, String group) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> last &lt;span style="color:#719e07">=&lt;/span> invoker;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>Filter&lt;span style="color:#719e07">&amp;gt;&lt;/span> filters &lt;span style="color:#719e07">=&lt;/span> ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (filters.size() &lt;span style="color:#719e07">&amp;gt;&lt;/span> 0) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> (&lt;span style="color:#dc322f">int&lt;/span> i &lt;span style="color:#719e07">=&lt;/span> filters.size() &lt;span style="color:#719e07">-&lt;/span> 1; i &lt;span style="color:#719e07">&amp;gt;=&lt;/span> 0; i &lt;span style="color:#719e07">--&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">final&lt;/span> Filter filter &lt;span style="color:#719e07">=&lt;/span> filters.get(i);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">final&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> next &lt;span style="color:#719e07">=&lt;/span> last;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> last &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">getInterface&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invoker.getInterface();
&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> URL &lt;span style="color:#268bd2">getUrl&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invoker.getUrl();
&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:#dc322f">boolean&lt;/span> &lt;span style="color:#268bd2">isAvailable&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invoker.isAvailable();
&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> Result &lt;span style="color:#268bd2">invoke&lt;/span>(Invocation invocation) &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">return&lt;/span> filter.invoke(next, invocation);
&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:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">destroy&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> invoker.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> String &lt;span style="color:#268bd2">toString&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invoker.toString();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> };
&lt;/span>&lt;/span>&lt;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> last;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="dubbo-filter的激活机制">Dubbo Filter的激活机制&lt;/h4>
&lt;p>通过上述代码我们可以看到，在&lt;code>buildInvokerChain&lt;/code>中,先获取所有已经激活的调用链，这里的调用链是已经排好序的。再通过Invoker来构造出一个Filter的调用链，最后构建出的调用链大致可以表示为：Filter1-&amp;gt;Filter2-&amp;gt;Filter3-&amp;gt;&amp;hellip;&amp;hellip;-&amp;gt;Invoker,下面我们来看一下，第一步中获取已经激活的调用链的详细流程：&lt;/p></description></item><item><title>回声测试</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/06/26/%E5%9B%9E%E5%A3%B0%E6%B5%8B%E8%AF%95/</link><pubDate>Tue, 26 Jun 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/06/26/%E5%9B%9E%E5%A3%B0%E6%B5%8B%E8%AF%95/</guid><description>&lt;p>回声测试用于检测服务是否可用。客户端通过 EchoService 来使用回声测试。EchoService 申明如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-Java" data-lang="Java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">EchoService&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * echo test.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param message message.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @return message.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Object &lt;span style="color:#268bd2">$echo&lt;/span>(Object message);
&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>用户通过 $echo 方法发起的请求，会按照正常请求的流程执行，能够测试整个调用是否通畅，监控系统可以使用回声测试来检测服务可用性。&lt;/p>
&lt;h2 id="使用范例">使用范例&lt;/h2>
&lt;p>所有服务引用自动实现 EchoService 接口，用户只需将服务引用强制转型为 EchoService，即可使用。配置和代码范例如下所示。
Spring 配置：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>&amp;lt;dubbo:reference id=&amp;#34;demoService&amp;#34; interface=&amp;#34;org.apache.dubbo.samples.echo.DemoService&amp;#34; /&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>代码：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-Java" data-lang="Java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 远程服务引用&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>DemoService demoService&lt;span style="color:#719e07">=&lt;/span> ctx.getBean(&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 强制转型为EchoService&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>EchoService echoService &lt;span style="color:#719e07">=&lt;/span> (EchoService) demoService;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 回声测试可用性&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>String status &lt;span style="color:#719e07">=&lt;/span> echoService.$echo(&lt;span style="color:#2aa198">&amp;#34;OK&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">assert&lt;/span>(status.equals(&lt;span style="color:#2aa198">&amp;#34;OK&amp;#34;&lt;/span>));
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="实现原理">实现原理&lt;/h2>
&lt;p>我们在配置服务引用时，并没有配置 EchoService 这个接口，为什么可以直接把服务引用转型为 EchoService 呢？
用户拿到的服务引用其实是一个 Proxy，Dubbo 在生成 Proxy 的时候，已经默认将 EchoService 这个接口加入到 Proxy 的接口列表中，所以用户拿到的 Proxy 都已经实现了 EchoService。生成代理相关代码如下：&lt;/p></description></item><item><title>以 Dubbo 为例，聊聊如何向开源项目做贡献</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/06/03/%E4%BB%A5-dubbo-%E4%B8%BA%E4%BE%8B%E8%81%8A%E8%81%8A%E5%A6%82%E4%BD%95%E5%90%91%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E5%81%9A%E8%B4%A1%E7%8C%AE/</link><pubDate>Sun, 03 Jun 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/06/03/%E4%BB%A5-dubbo-%E4%B8%BA%E4%BE%8B%E8%81%8A%E8%81%8A%E5%A6%82%E4%BD%95%E5%90%91%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E5%81%9A%E8%B4%A1%E7%8C%AE/</guid><description>&lt;p>Github 上有众多优秀的开源项目，大多数 IT 从业者将其当做了予取予求的工具库，遇到什么需求，先去 Github 搜一把，但有没有想过有一天自己也可以给开源事业做一些贡献呢？本文将会以 dubbo 项目为例，向你阐释，给开源项目做贡献并不是一件难事。&lt;/p>
&lt;h2 id="1-为何要给开源贡献力量">1 为何要给开源贡献力量&lt;/h2>
&lt;p>为开源项目做贡献得到的收益是多方面的，为了让你有足够的信心加入到开源项目中，我在文章最开始列举出它的诸多好处。&lt;/p>
&lt;h3 id="11-巩固技能">1.1 巩固技能&lt;/h3>
&lt;p>无论你是提交代码，撰写文档，提交 Issue，组织活动，当你切身参与到一个开源项目中，相关的技能都会得到历练，并且在开源项目中找到自己的位置。一方面，日常工作中我们中的大多数人接触到的是业务场景，并没有太多机会接触到基础架构组件，开源项目为我们提供了一个平台，在这里，你可以尽情挑选自己熟悉的项目为它添砖加瓦（以 Dubbo 为例，并不是所有 IT 公司都有能力自研服务治理框架）；另一方面，你所提交的代码，会有管理员协助审核，他们会给出专业的建议，更好的代码规范以及更优的编程思路最终都会变成你的经验。&lt;/p>
&lt;h3 id="12-结交朋友">1.2 结交朋友&lt;/h3>
&lt;p>开源社区为你提供了一个平台，在这里，你可以认识很多纯粹的技术爱好者，开源贡献者是最符合 geek 定义的那群人，你所接触到的往往是某个领域最厉害的那批人。&lt;/p>
&lt;h3 id="13-建立口碑">1.3 建立口碑&lt;/h3>
&lt;p>这是一个很好的展示个人实力的地方，俗话说：talk is cheap，show me the code. 作为技术人员，没有什么比一个漂亮的 Github 主页更有说服力的了。如果你能够为开源项目做出可观的贡献，你也将收获到业界的知名度，此时开源项目的成就和你是密不可分的。&lt;/p>
&lt;h3 id="14-传承开源精神">1.4 传承开源精神&lt;/h3>
&lt;p>只有源源不断的贡献者给开源项目添砖加瓦，才可以为 Github 一类的开源社区形成良好的开源风气。否则，只有输出没有输入，开源会失去活力。&lt;/p>
&lt;h3 id="15-养成习惯">1.5 养成习惯&lt;/h3>
&lt;p>相信我，一旦养成了每天提交代码的习惯，就像你不想中断打卡一样，你绝不想中断 commit。不止有英语打卡，健身打卡，还有开源打卡！&lt;/p>
&lt;h2 id="2-贡献代码时的一些疑难杂症">2 贡献代码时的一些疑难杂症&lt;/h2>
&lt;p>如果你是一名开源界的新手，可能会对贡献的流程心生畏惧。比如：我该怎么修改代码并提交？我的代码要是存在bug怎么办？我的代码别人会不会很 low？我该如何寻找合适的开源项目？开源社区那么多的工具和词汇都是什么意思？&lt;/p>
&lt;p>文章的第二部分将从一个&lt;strong>小白&lt;/strong>的角度，介绍一下开源中的一些常见问题。&lt;/p>
&lt;h3 id="21-git-常规操作">2.1 git 常规操作&lt;/h3>
&lt;p>一般而言，我们选择使用 git 来作为版本管理的工具，你不一定要非常熟练的使用它，在我看来掌握 clone，add，commit，pull，push 即可，遇到复杂的场景，你还有谷歌。&lt;/p>
&lt;p>&lt;strong>fork 与 clone&lt;/strong>&lt;/p>
&lt;p>如果你只是想下载源码，查看他的源码实现，使用 Clone or download 按钮即可。&lt;/p>
&lt;p>如果你想要给开源项目做改动，并且最终请求合并，让开源项目存在你贡献的代码，就应该使用 fork。&lt;/p>
&lt;p>fork 将会复制一份当前主分支的代码进入到你的仓库中，之后你所有的修改，应当基于自己的仓库进行，在功能开发/bug 修复之后，可以使用你的仓库向源仓库提交 pull request。只有源仓库的管理员才有权利合并你的请求。&lt;/p></description></item><item><title>Dubbo 外部化配置</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/05/21/dubbo-%E5%A4%96%E9%83%A8%E5%8C%96%E9%85%8D%E7%BD%AE/</link><pubDate>Mon, 21 May 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/05/21/dubbo-%E5%A4%96%E9%83%A8%E5%8C%96%E9%85%8D%E7%BD%AE/</guid><description>&lt;h1 id="dubbo-外部化配置">Dubbo 外部化配置&lt;/h1>
&lt;h2 id="外部化配置">外部化配置&lt;/h2>
&lt;p>在&lt;a href="https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/08/07/dubbo-%E6%B3%A8%E8%A7%A3%E9%A9%B1%E5%8A%A8/">Dubbo 注解驱动&lt;/a>例子中，无论是服务提供方，还是服务消费方，均需要转配相关配置Bean：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Bean&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> ApplicationConfig &lt;span style="color:#268bd2">applicationConfig&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ApplicationConfig applicationConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ApplicationConfig();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationConfig.setName(&lt;span style="color:#2aa198">&amp;#34;dubbo-annotation-consumer&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> applicationConfig;
&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>ProviderConfiguration&lt;/code> 和 &lt;code>ConsumerConfiguration&lt;/code> 这样的 Spring &lt;code>@Configuration&lt;/code> Bean 成本并不高，不过通过 Java Code 的方式定义配置 Bean，或多或少是一种 Hard Code（硬编码）的行为，缺少弹性。&lt;/p>
&lt;p>尽管在 Spring 应用中，可以通过 &lt;code>@Value&lt;/code> 或者 &lt;code>Environment&lt;/code> 的方式获取外部配置，其代码简洁性以及类型转换灵活性存在明显的不足。因此，Spring Boot 提出了外部化配置（External Configuration）的感念，即通过程序以外的配置源，动态地绑定指定类型。&lt;/p>
&lt;p>随着 Spring Boot / Spring Cloud 应用的流行，开发人员逐渐地接受并且使用 Spring Boot 外部化配置（External Configuration），即通过 &lt;code>application.properties&lt;/code> 或者 &lt;code>bootstrap.properties&lt;/code> 装配配置 Bean。&lt;/p>
&lt;p>下列表格记录了 Dubbo 内置配置类：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th style="text-align: left">配置类&lt;/th>
 &lt;th style="text-align: left">标签&lt;/th>
 &lt;th style="text-align: left">用途&lt;/th>
 &lt;th style="text-align: left">解释&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td style="text-align: left">&lt;code>ProtocolConfig&lt;/code>&lt;/td>
 &lt;td style="text-align: left">&lt;code>&amp;lt;dubbo:protocol/&amp;gt;&lt;/code>&lt;/td>
 &lt;td style="text-align: left">协议配置&lt;/td>
 &lt;td style="text-align: left">用于配置提供服务的协议信息，协议由提供方指定，消费方被动接受&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&lt;code>ApplicationConfig&lt;/code>&lt;/td>
 &lt;td style="text-align: left">&lt;code>&amp;lt;dubbo:application/&amp;gt;&lt;/code>&lt;/td>
 &lt;td style="text-align: left">应用配置&lt;/td>
 &lt;td style="text-align: left">用于配置当前应用信息，不管该应用是提供者还是消费者&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&lt;code>ModuleConfig&lt;/code>&lt;/td>
 &lt;td style="text-align: left">&lt;code>&amp;lt;dubbo:module/&amp;gt;&lt;/code>&lt;/td>
 &lt;td style="text-align: left">模块配置&lt;/td>
 &lt;td style="text-align: left">用于配置当前模块信息，可选&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&lt;code>RegistryConfig&lt;/code>&lt;/td>
 &lt;td style="text-align: left">&lt;code>&amp;lt;dubbo:registry/&amp;gt;&lt;/code>&lt;/td>
 &lt;td style="text-align: left">注册中心配置&lt;/td>
 &lt;td style="text-align: left">用于配置连接注册中心相关信息&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&lt;code>MonitorConfig&lt;/code>&lt;/td>
 &lt;td style="text-align: left">&lt;code>&amp;lt;dubbo:monitor/&amp;gt;&lt;/code>&lt;/td>
 &lt;td style="text-align: left">监控中心配置&lt;/td>
 &lt;td style="text-align: left">用于配置连接监控中心相关信息，可选&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&lt;code>ProviderConfig&lt;/code>&lt;/td>
 &lt;td style="text-align: left">&lt;code>&amp;lt;dubbo:provider/&amp;gt;&lt;/code>&lt;/td>
 &lt;td style="text-align: left">提供方配置&lt;/td>
 &lt;td style="text-align: left">当 ProtocolConfig 和 ServiceConfig 某属性没有配置时，采用此缺省值，可选&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&lt;code>ConsumerConfig&lt;/code>&lt;/td>
 &lt;td style="text-align: left">&lt;code>&amp;lt;dubbo:consumer/&amp;gt;&lt;/code>&lt;/td>
 &lt;td style="text-align: left">消费方配置&lt;/td>
 &lt;td style="text-align: left">当 ReferenceConfig 某属性没有配置时，采用此缺省值，可选&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&lt;code>MethodConfig&lt;/code>&lt;/td>
 &lt;td style="text-align: left">&lt;code>&amp;lt;dubbo:method/&amp;gt;&lt;/code>&lt;/td>
 &lt;td style="text-align: left">方法配置&lt;/td>
 &lt;td style="text-align: left">用于 ServiceConfig 和 ReferenceConfig 指定方法级的配置信息&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&lt;code>ArgumentConfig&lt;/code>&lt;/td>
 &lt;td style="text-align: left">&lt;code>&amp;lt;dubbo:argument/&amp;gt;&lt;/code>&lt;/td>
 &lt;td style="text-align: left">参数配置&lt;/td>
 &lt;td style="text-align: left">用于指定方法参数配置&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>通过申明对应的 Spring 扩展标签，在 Spring 应用上下文中将自动生成相应的配置 Bean。&lt;/p></description></item><item><title>Dubbo 博客文档中文排版指南</title><link>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/01/01/dubbo-%E5%8D%9A%E5%AE%A2%E6%96%87%E6%A1%A3%E4%B8%AD%E6%96%87%E6%8E%92%E7%89%88%E6%8C%87%E5%8D%97/</link><pubDate>Mon, 01 Jan 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3202--dubbo.netlify.app/zh-cn/blog/2018/01/01/dubbo-%E5%8D%9A%E5%AE%A2%E6%96%87%E6%A1%A3%E4%B8%AD%E6%96%87%E6%8E%92%E7%89%88%E6%8C%87%E5%8D%97/</guid><description>&lt;h1 id="dubbo-博客文档中文排版指南">Dubbo 博客文档中文排版指南&lt;/h1>
&lt;p>[TOC]&lt;/p>
&lt;h2 id="空格">空格&lt;/h2>
&lt;p>「有研究显示，打字的时候不喜欢在中文和英文之间加空格的人，感情路都走得很辛苦，有七成的比例会在 34 岁的时候跟自己不爱的人结婚，而其余三成的人最后只能把遗产留给自己的猫。毕竟爱情跟书写都需要适时地留白。与大家共勉之。」—— &lt;a href="https://github.com/vinta/pangu.js">vinta/paranoid-auto-spacing&lt;/a>&lt;/p>
&lt;h3 id="中英文之间需要增加空格">中英文之间需要增加空格&lt;/h3>
&lt;p>正确：&lt;/p>
&lt;blockquote>
&lt;p>在 LeanCloud 上，数据存储是围绕 &lt;code>AVObject&lt;/code> 进行的。&lt;/p>
&lt;/blockquote>
&lt;p>错误：&lt;/p>
&lt;blockquote>
&lt;p>在LeanCloud上，数据存储是围绕&lt;code>AVObject&lt;/code>进行的。&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>在 LeanCloud上，数据存储是围绕&lt;code>AVObject&lt;/code> 进行的。&lt;/p>
&lt;/blockquote>
&lt;p>完整的正确用法：&lt;/p>
&lt;blockquote>
&lt;p>在 LeanCloud 上，数据存储是围绕 &lt;code>AVObject&lt;/code> 进行的。每个 &lt;code>AVObject&lt;/code> 都包含了与 JSON 兼容的 key-value 对应的数据。数据是 schema-free 的，你不需要在每个 &lt;code>AVObject&lt;/code> 上提前指定存在哪些键，只要直接设定对应的 key-value 即可。&lt;/p>
&lt;/blockquote>
&lt;p>例外：「豆瓣FM」等产品名词，按照官方所定义的格式书写。&lt;/p>
&lt;h3 id="中文与数字之间需要增加空格">中文与数字之间需要增加空格&lt;/h3>
&lt;p>正确：&lt;/p>
&lt;blockquote>
&lt;p>今天出去买菜花了 5000 元。&lt;/p>
&lt;/blockquote>
&lt;p>错误：&lt;/p>
&lt;blockquote>
&lt;p>今天出去买菜花了 5000元。&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>今天出去买菜花了5000元。&lt;/p>
&lt;/blockquote>
&lt;h3 id="数字与单位之间需要增加空格">数字与单位之间需要增加空格&lt;/h3>
&lt;p>正确：&lt;/p>
&lt;blockquote>
&lt;p>我家的光纤入户宽带有 10 Gbps，SSD 一共有 20 TB。&lt;/p>
&lt;/blockquote>
&lt;p>错误：&lt;/p>
&lt;blockquote>
&lt;p>我家的光纤入户宽带有 10Gbps，SSD 一共有 10TB。&lt;/p>
&lt;/blockquote>
&lt;p>例外：度／百分比与数字之间不需要增加空格：&lt;/p>
&lt;p>正确：&lt;/p>
&lt;blockquote>
&lt;p>今天是 233° 的高温。&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>新 MacBook Pro 有 15% 的 CPU 性能提升。&lt;/p></description></item></channel></rss>