This is an automated email from the ASF dual-hosted git repository.

jerrick pushed a commit to branch asf-site
in repository https://gitbox.apache.org/repos/asf/incubator-dubbo-website.git


The following commit(s) were added to refs/heads/asf-site by this push:
     new 40bcee1  build
40bcee1 is described below

commit 40bcee17793df20281bcb9b6edcbf1ed626f54f2
Author: zhuyong <yong.z...@alibaba-inc.com>
AuthorDate: Fri Aug 10 16:33:30 2018 +0800

    build
---
 blog/zh-cn/dubbo-loadbalance.md                    | 265 ++++++++++
 blog/zh-cn/dubbo-new-async.md                      | 556 +++++++++++++++++++++
 build/0d69326d69fd47b9d99a.js                      |   6 -
 ...8f548578cb3c9f5b.js => 5dfcc3831ea1057a5367.js} |   2 +-
 build/fca877d6ab8a5dbba7ca.js                      |   6 +
 build/page.js                                      |   2 +-
 md_json/blog.json                                  |   4 +-
 site_config/blog.js                                |   2 +-
 8 files changed, 832 insertions(+), 11 deletions(-)

diff --git a/blog/zh-cn/dubbo-loadbalance.md b/blog/zh-cn/dubbo-loadbalance.md
index e69de29..65aa757 100644
--- a/blog/zh-cn/dubbo-loadbalance.md
+++ b/blog/zh-cn/dubbo-loadbalance.md
@@ -0,0 +1,265 @@
+# Dubbo的负载均衡
+
+# 背景
+
+Dubbo是一个分布式服务框架,能避免单点故障和支持服务的横向扩容。一个服务通常会部署多个实例。如何从多个服务提供者组成的集群中挑选出一个进行调用,就涉及到一个负载均衡的策略。
+
+# 几个概念
+
+在讨论负载均衡之前,我想先解释一下这3个概念。
+
+1. 负载均衡
+2. 集群容错
+3. 服务路由
+
+这3个概念容易混淆。他们都描述了怎么从多个Provider中选择一个来进行调用。那他们到底有什么区别呢?下面我来举一个简单的例子,把这几个概念阐述清楚吧。
+
+有一个Dubbo的用户服务,在北京部署了10个,在上海部署了20个。一个杭州的服务消费方发起了一次调用,然后发生了以下的事情:
+
+1. 根据配置的路由规则,如果杭州发起的调用,会路由到比较近的上海的20个Provider。
+2. 根据配置的随机负载均衡策略。在20个Provider中随机选择了一个来调用,假设随机到了第7个Provider。
+3. 结果调用第7个Provider失败了。
+4. 根据配置的Failover集群容错模式,重试其他服务器。
+5. 重试了第13个Provider,调用成功。
+
+上面的第1,2,4步骤就分别对应了路由,负载均衡和集群容错。 
Dubbo中,先通过路由,从多个Provider中按照路由规则,选出一个子集。再根据负载均衡从子集中选出一个Provider进行本次调用。如果调用失败了,根据集群容错策略,进行重试或定时重发或快速失败等。
 可以看到Dubbo中的路由,负载均衡和集群容错发生在一次RPC调用的不同阶段。最先是路由,然后是负载均衡,最后是集群容错。 
本文档只讨论负载均衡,路由和集群容错在其他的文档中进行说明。
+
+# Dubbo内置负载均衡策略
+
+Dubbo内置了4种负载均衡策略:
+
+1. RandomLoadBalance:随机负载均衡。随机的选择一个。是Dubbo的**默认**负载均衡策略。
+2. RoundRobinLoadBalance:轮询负载均衡。轮询选择一个。
+3. 
LeastActiveLoadBalance:最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
+4. ConsistentHashLoadBalance:一致性哈希负载均衡。相同参数的请求总是落在同一台机器上。
+
+### 1.随机负载均衡
+
+顾名思义,随机负载均衡策略就是从多个Provider中随机选择一个。但是Dubbo中的随机负载均衡有一个权重的概念,即按照权重设置随机概率。比如说,有10个Provider,并不是说,每个Provider的概率都是一样的,而是要结合这10个provider的权重来分配概率。
+
+Dubbo中,可以对Provider设置权重。比如机器性能好的,可以设置大一点的权重,性能差的,可以设置小一点的权重。权重会对负载均衡产生影响。可以在Dubbo
 Admin中对provider进行权重的设置。
+
+**基于权重的负载均衡算法**
+
+随机策略会先判断所有的invoker的权重是不是一样的,如果都是一样的,那么处理就比较简单了。使用random.nexInt(length)就可以随机生成一个invoker的序号,根据序号选择对应的invoker。如果没有在Dubbo
 Admin中对服务提供者设置权重,那么所有的invoker的权重就是一样的,默认是100。 如果权重不一样,那就需要结合权重来设置随机概率了。算法大概如下: 
假如有4个invoker
+
+| invoker | weight |
+| ------- | ------ |
+| A       | 10     |
+| B       | 20     |
+| C       | 20     |
+| D       | 30     |
+
+A,B,C和D总的权重是10 + 20 + 20 + 30 = 80。将80个数分布在如下的图中:
+
+```
++-----------------------------------------------------------------------------------+
+|          |                    |                    |                         
     |
++-----------------------------------------------------------------------------------+
+1          10                   30                   50                        
     80            
+
+|-----A----|---------B----------|----------C---------|---------------D--------------|
+
+
+---------------------15
+
+-------------------------------------------37
+
+-----------------------------------------------------------54
+```
+
+上面的图中一共有4块区域,长度分别是A,B,C和D的权重。使用random.nextInt(10 + 20 + 20 + 
30),从80个数中随机选择一个。然后再判断该数分布在哪个区域。比如,如果随机到37,37是分布在C区域的,那么就选择inboker 
C。15是在B区域,54是在D区域。
+
+**随机负载均衡源码**
+
+下面是随机负载均衡的源码,为了方便阅读和理解,我把无关部分都去掉了。
+
+```
+public class RandomLoadBalance extends AbstractLoadBalance {
+
+    private final Random random = new Random();
+
+    protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, 
Invocation invocation) {
+        int length = invokers.size();      // invoker总数
+        int totalWeight = 0;               // 所有invoker的权重的和
+        
+        // 判断是不是所有的invoker的权重都是一样的
+        // 如果权重都一样,就简单了。直接用Random生成索引就可以了。
+        boolean sameWeight = true; 
+        for (int i = 0; i < length; i++) {
+            int weight = getWeight(invokers.get(i), invocation);
+            totalWeight += weight; // Sum
+            if (sameWeight && i > 0 && weight != getWeight(invokers.get(i - 
1), invocation)) {
+                sameWeight = false;
+            }
+        }
+        
+        if (totalWeight > 0 && !sameWeight) {
+            // 如果不是所有的invoker权重都相同,那么基于权重来随机选择。权重越大的,被选中的概率越大
+            int offset = random.nextInt(totalWeight);
+            for (int i = 0; i < length; i++) {
+                offset -= getWeight(invokers.get(i), invocation);
+                if (offset < 0) {
+                    return invokers.get(i);
+                }
+            }
+        }
+        // 如果所有invoker权重相同
+        return invokers.get(random.nextInt(length));
+    }
+}
+```
+
+### 2.轮循负载均衡
+
+轮询负载均衡,就是依次的调用所有的Provider。和随机负载均衡策略一样,轮询负载均衡策略也有权重的概念。 
轮询负载均衡算法可以让RPC调用严格按照我们设置的比例来分配。不管是少量的调用还是大量的调用。但是轮询负载均衡算法也有不足的地方,存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上,导致整个系统变慢。
+
+### 3.最少活跃调用数负载均衡
+
+官方解释:
+
+> 最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差,使慢的机器收到更少。
+
+ 这个解释好像说的不是太明白。知道了目的是让慢的机器收到更少,但具体怎么实现的还是不太清楚。让我来举个例子吧: 
例如,每个服务维护一个活跃数计数器。当A机器开始处理请求,该计数器加1,此时A还未处理完成。若处理完毕则计数器减1。而B机器接受到请求后很快处理完毕。那么A,B的活跃数分别是1,0。当又产生了一个新的请求,则选择B机器去执行(B活跃数最小),这样使慢的机器A收到少的请求。
+
+处理一个新的请求时,Consumer会检查所有Provider的活跃数,如果具有最小活跃数的invoker只有一个,直接返回该Invoker:
+
+```
+if (leastCount == 1) {
+    // 如果只有一个最小则直接返回
+    return invokers.get(leastIndexs[0]);
+}
+```
+
+如果最小活跃数的invoker有多个,且权重不相等同时总权重大于0,这时随机生成一个权重,范围在0,totalWeight 
间内。最后根据随机生成的权重,来选择invoker。
+
+```
+if (! sameWeight && totalWeight > 0) {
+    // 如果权重不相同且权重大于0则按总权重数随机
+    int offsetWeight = random.nextInt(totalWeight);
+    // 并确定随机值落在哪个片断上
+    for (int i = 0; i < leastCount; i++) {
+        int leastIndex = leastIndexs[i];
+        offsetWeight -= getWeight(invokers.get(leastIndex), invocation);
+        if (offsetWeight <= 0)
+            return invokers.get(leastIndex);
+    }
+}
+```
+
+### 4.一致性Hash算法
+
+使用一致性 Hash,让相同参数的请求总是发到同一提供者。 当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。 
算法参见:<http://en.wikipedia.org/wiki/Consistent_hashing>。
+
+缺省只对第一个参数Hash,如果要修改,请配置:
+
+```
+<dubbo:parameter key="hash.arguments" value="0,1" />
+```
+
+缺省用160份虚拟节点,如果要修改,请配置:
+
+```
+<dubbo:parameter key="hash.nodes" value="320" />
+```
+
+一致性Hash算法可以和缓存机制配合起来使用。比如有一个服务getUserInfo(String 
userId)。设置了Hash算法后,相同的userId的调用,都会发送到同一个提供者。这个提供者上可以把用户数据在内存中进行缓存,减少访问数据库或分布式缓存的次数。如果业务上允许这部分数据有一段时间的不一致,可以考虑这种做法。减少对数据库,缓存等中间件的依赖和访问次数,同时减少了网络IO操作,提高系统性能。
+
+# 负载均衡配置
+
+如果不指定负载均衡,默认使用随机负载均衡。我们也可以根据自己的需要,显式指定一个负载均衡。 
可以在多个地方类来配置负载均衡,比如Provider端,Consumer端,服务级别,方法级别等。
+
+### 服务端服务级别
+
+```
+<dubbo:service interface="..." loadbalance="roundrobin" />
+```
+
+该服务的所有方法都使用roundrobin负载均衡。
+
+### 客户端服务级别
+
+```
+<dubbo:reference interface="..." loadbalance="roundrobin" />
+```
+
+该服务的所有方法都使用roundrobin负载均衡。
+
+### 服务端方法级别
+
+```
+<dubbo:service interface="...">
+    <dubbo:method name="hello" loadbalance="roundrobin"/>
+</dubbo:service>
+```
+
+只有该服务的hello方法使用roundrobin负载均衡。
+
+### 客户端方法级别
+
+```
+<dubbo:reference interface="...">
+    <dubbo:method name="hello" loadbalance="roundrobin"/>
+</dubbo:reference>
+```
+
+只有该服务的hello方法使用roundrobin负载均衡。
+
+和Dubbo其他的配置类似,多个配置是有覆盖关系的:
+
+1. 方法级优先,接口级次之,全局配置再次之。
+2. 如果级别一样,则消费方优先,提供方次之。
+
+所以,上面4种配置的优先级是:
+
+1. 客户端方法级别配置。
+2. 客户端接口级别配置。
+3. 服务端方法级别配置。
+4. 服务端接口级别配置。
+
+# 扩展负载均衡
+
+Dubbo的4种负载均衡的实现,大多数情况下能满足要求。有时候,因为业务的需要,我们可能需要实现自己的负载均衡策略。本章只说明如何配置负载均衡算法。关于Dubbo扩展机制的更多内容,请前往[Dubbo可扩展机制实战](https://lark.alipay.com/aliware_articles/vtpf9h/pe9pyr)。
+
+1. 实现LoadBalance接口 以下是Dubbo的LoadBalance接口:
+
+```
+@SPI(RandomLoadBalance.NAME)
+public interface LoadBalance {
+    @Adaptive("loadbalance")
+    <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation 
invocation) throws RpcException;
+}
+```
+
+这是SPI的接口,select方法的参数如下:
+
+- invokers: 所有的服务提供者列表。
+- url: 一些配置信息,比如接口名,是否check,序列化方式。
+- invocation: RPC调用的信息,包括方法名,方法参数类型,方法参数。 
下面是我们自己实现的一个LoadBalance,实现很简单,选择第一个invoker:
+
+```
+package com.demo.dubbo;
+public class DemoLoadBalance implements LoadBalance {
+    @Override
+    public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, 
Invocation invocation) throws RpcException {
+        System.out.println("[DemoLoadBalance]Select the first invoker...");
+        return invokers.get(0);
+    }
+}
+```
+
+1. 添加资源文件 
添加文件:`src/main/resource/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.LoadBalance`。这是一个简单的文本文件。文件内容如下:
+
+```
+demo=my=com.demo.dubbo.DemoLoadBalance
+```
+
+1. 配置使用自定义LoadBalance
+
+```
+<dubbo:reference id="helloService" 
interface="com.demo.dubbo.api.IHelloService" loadbalance="demo" />
+```
+
+在consumer端的dubbo:reference中配置<loadbalance="demo">
+
+经过上面的3个步骤,我们编写了一个自定义的LoadBalance,并告诉Dubbo使用它了。启动Dubbo,我们就能看到Dubbo已经使用了自定义的DemoLoadBalance。
\ No newline at end of file
diff --git a/blog/zh-cn/dubbo-new-async.md b/blog/zh-cn/dubbo-new-async.md
index e69de29..48c7a13 100644
--- a/blog/zh-cn/dubbo-new-async.md
+++ b/blog/zh-cn/dubbo-new-async.md
@@ -0,0 +1,556 @@
+# 如何基于Dubbo实现全异步调用链
+
+基于Dubbo实现全异步编程,是在2.7.0版本中对现有异步方式增强后新引入的功能。本文先是回顾2.6.x及之前版本对异步的支持情况及存在的问题,引出了2.7.0版本基于CompletableFuture做了哪些针对性的增强,通过几个示例详细阐述了增强后的异步编程的使用方式,最后总结了引入异步模式带来的新问题及Dubbo的解决方法。通过阅读这篇文章,可以很容易的基于Dubbo2.7.0+版本实现一个全异步的远程服务调用链路。
+
+## 2.6.x版本之前的异步方式
+
+在2.6.x及之前的版本提供了一定的异步编程能力,包括Consumer端[异步调用](http://dubbo.apache.org/books/dubbo-user-book/demos/async-call.html)、[参数回调](http://dubbo.apache.org/books/dubbo-user-book/demos/callback-parameter.html)、[事件通知](http://dubbo.apache.org/books/dubbo-user-book/demos/events-notify.html)等,在上面的文档链接中有关于使用方式的简单介绍和Demo。
+
+但当前的异步方式存在以下问题:
+
+- Future获取方式不够直接
+- Future接口无法实现自动回调,而自定义ResponseFuture虽支持回调但支持的异步场景有限,如不支持Future间的相互协调或组合等
+- 不支持Provider端异步
+
+以Consumer端异步使用方式为例:
+
+1. 定义一个普通的同步接口并声明支持异步调用
+
+```
+public interface FooService {
+    String findFoo(String name);
+}
+```
+
+```
+<dubbo:reference id="fooService" interface="com.alibaba.foo.FooService">
+      <dubbo:method name="findFoo" async="true" />
+</dubbo:reference>
+```
+
+1. 通过RpcContext获取Future
+
+```
+// 此调用会立即返回null
+fooService.findFoo(fooId);
+// 拿到调用的Future引用,当结果返回后,会被通知和设置到此Future
+Future<Foo> fooFuture = RpcContext.getContext().getFuture();
+fooFuture.get();
+```
+
+或
+
+```
+// 此调用会立即返回null
+fooService.findFoo(fooId);
+// 拿到Dubbo内置的ResponseFuture并设置回调
+ResponseFuture future = 
((FutureAdapter)RpcContext.getContext().getFuture()).getFuture();
+future.setCallback(new ResponseCallback() {
+    @Override
+    public void done(Object response) {
+        System.out.print(response);
+    }
+
+    @Override
+    public void caught(Throwable exception) {
+        exception.printStackTrace();
+    }
+});
+```
+
+从这个简单的示例我们可以体会到一些使用中的不便之处:
+
+1. findFoo的同步接口,不能直接返回代表异步结果的Future,通过RpcContext进一步获取。
+2. Future只支持阻塞式的get()接口获取结果。
+3. 
通过获取内置的ResponseFuture接口,可以设置回调。但获取ResponseFuture的API使用不便,且仅支持设置回调其他异步场景均不支持,如多个Future协同工作的场景等。
+
+## 2.7.0基于CompletableFuture的增强
+
+了解Java中Future演进历史的同学应该知道,Dubbo 2.6.x及之前版本中使用的Future是在java 
5中引入的,所以存在以上一些功能设计上的问题,而在java 8中引入的CompletableFuture进一步丰富了Future接口,很好的解决了这些问题。
+
+Dubbo在2.7.0版本已经升级了对Java 8的支持,同时基于CompletableFuture对当前的异步功能进行了增强。
+
+1. 支持直接定义返回CompletableFuture的服务接口。通过这种类型的接口,我们可以更自然的实现Consumer、Provider端的异步编程。
+
+   
+
+   ```
+   public interface AsyncService {
+       CompletableFuture<String> sayHello(String name);
+   }
+   ```
+
+   
+
+   
+
+2. 如果你不想将接口的返回值定义为Future类型,或者存在定义好的同步类型接口,则可以额外定义一个异步接口并提供Future类型的方法。
+
+   
+
+   ```
+   public interface GreetingsService {
+       String sayHi(String name);
+   }
+   ```
+
+   
+
+   ```
+   @AsyncFor(AsyncService.class)
+   public interface GrettingServiceAsync extends GreetingsService {
+       CompletableFuture<String> sayHiAsync(String name);
+   }
+   ```
+
+   
+
+   
这样,Provider可以只实现sayHi方法;而Consumer通过直接调用sayHiAsync可以拿到一个Future实例,Dubbo框架在Provider端会自动转换为对sayHi方法的调用。
+
+   
+
+   为每个同步方法提供一个异步方法定义会比较麻烦,更进一步的,利用Dubbo生态中的[Annotation 
Processor实现](https://github.com/dubbo/dubbo-async-processor),可以自动帮我们自动生成异步方法定义。
+
+3. 同样的,如果你的原始接口定义不是Future类型的返回值,Provider端异步也提供了类似Servlet3.0里的Async 
Servlet的编程接口: `RpcContext.startAsync()`。
+
+   
+
+   ```
+   public interface AsyncService {
+       String sayHello(String name);
+   }
+   ```
+
+   
+
+   ```
+   public class AsyncServiceImpl implements AsyncService {
+       public String sayHello(String name) {
+           final AsyncContext asyncContext = RpcContext.startAsync();
+           new Thread(() -> {
+               asyncContext.write("Hello " + name + ", response from 
provider.");
+           }).start();
+           return null;
+       }
+   }
+   ```
+
+   
+
+   
在方法体的开始`RpcContext.startAsync()`启动异步,并开启新线程异步的执行业务逻辑,在耗时操作完成后通过`asyncContext.write`将结果写回。
+
+4. RpcContext直接返回CompletableFuture
+
+   
+
+   ```
+   CompletableFuture<String> f = 
RpcContext.getContext().getCompletableFuture();
+   ```
+
+以上所有的增强,是在兼容已有异步编程的基础上进行的,因此基于2.6.x版本编写的异步程序不用做任何改造即可顺利编译通过。
+
+接下来,我们通过几个示例看一下如何实现一个全异步的Dubbo服务调用链。
+
+## 示例1:CompletableFuture类型接口
+
+CompletableFuture类型的接口既可以用作同步调用,也可以实现Consumer或Provider的异步调用。本示例实现了Consumer和Provider端异步调用,代码参见[dubbo-samples-async-original-future](https://github.com/dubbo/dubbo-samples/tree/samples-for-2.7.0-SNAPSHOT/dubbo-samples-async-original-future)。
+
+1. 定义接口
+
+   
+
+   ```
+   public interface AsyncService {
+       CompletableFuture<String> sayHello(String name);
+   }
+   ```
+
+   
+
+   注意接口的返回类型是`CompletableFuture<String>`。
+
+2. Provider端
+
+   
+
+   - 实现
+
+     
+
+     ```
+     public class AsyncServiceImpl implements AsyncService {
+         public CompletableFuture<String> sayHello(String name) {
+             return CompletableFuture.supplyAsync(() -> {
+                 try {
+                     Thread.sleep(5000);
+                 } catch (InterruptedException e) {
+                     e.printStackTrace();
+                 }
+                 return "async response from provider.";
+             });
+         }
+     }
+     ```
+
+     
+
+     可以看到这里通过supplyAsync将业务代码切换到了新的线程执行,因此实现了Provider端异步。
+
+   - 配置
+
+     
+
+     ```
+     <bean id="asyncService" 
class="com.alibaba.dubbo.samples.async.impl.AsyncServiceImpl"/>
+     <dubbo:service 
interface="com.alibaba.dubbo.samples.async.api.AsyncService" 
ref="asyncService"/>
+     ```
+
+     
+
+     配置方式和普通接口是一样。
+
+3. Consumer端
+
+   
+
+   - 配置
+
+   
+
+   ```
+   <dubbo:reference id="asyncService" timeout="10000" 
interface="com.alibaba.dubbo.samples.async.api.AsyncService"/>
+   ```
+
+   
+
+   ​   配置方式和普通接口是一样。
+
+   
+
+   - 调用远程服务
+
+   
+
+   ```
+   public static void main(String[] args) throws Exception {
+           ClassPathXmlApplicationContext context = new 
ClassPathXmlApplicationContext(new 
String[]{"META-INF/spring/async-consumer.xml"});
+           context.start();
+           final AsyncService asyncService = (AsyncService) 
context.getBean("asyncService");
+       
+           CompletableFuture<String> future = asyncService.sayHello("async 
call request");
+           future.whenComplete((v, t) -> {
+               if (t != null) {
+                   t.printStackTrace();
+               } else {
+                   System.out.println("Response: " + v);
+               }
+           });
+           System.out.println("Executed before response return.");
+           System.in.read();
+       }
+   ```
+
+   
+
+   `CompletableFuture<String> future = asyncService.sayHello("async call 
request");`很自然的返回了Future示例,这样就实现了Consumer端的异步服务调用。
+
+## 示例2:同步接口使用Annotation Processor
+
+这个示例演示了如何在只定义同步接口的基础上,使用Annotation 
Processor实现Consumer端异步方服务调用,具体代码参见地址[dubbo-samples-async-generated-future](https://github.com/dubbo/dubbo-samples/tree/samples-for-2.7.0-SNAPSHOT/dubbo-samples-async-generated-future)
+
+1. 定义接口
+
+   
+
+   ```
+   @DubboAsync
+   public interface GreetingsService {
+       String sayHi(String name);
+   }
+   ```
+
+   
+
+   这是一个普通的Dubbo服务接口定义。注意,使用Annotation Processor要加上@DubboAsync注解。
+
+   
+
+   ```
+   <dependency>
+       <groupId>com.alibaba</groupId>
+       <artifactId>dubbo-async-processer</artifactId>
+       <version>1.0.0-SNAPSHOT</version>
+   </dependency>
+   <plugin>
+       <groupId>org.apache.maven.plugins</groupId>
+       <artifactId>maven-compiler-plugin</artifactId>
+       <version>3.7.0</version>
+       <configuration>
+           <source>1.8</source>
+           <target>1.8</target>
+           <annotationProcessorPaths>
+               <path>
+                   <groupId>com.alibaba</groupId>
+                   <artifactId>dubbo-async-processer</artifactId>
+                   <version>1.0.0-SNAPSHOT</version>
+               </path>
+           </annotationProcessorPaths>
+       </configuration>
+   </plugin>
+   ```
+
+   
+
+   
以上是引入dubbo-async-processer处理器的Maven依赖,通常定义接口(提供API)的开发者将以上依赖加到工程中,这样在做API打包的时候,API中会自动生成以下接口定义:
+
+   
+
+   ```
+   /**
+   * Generated by dubbo-async-processer
+   */
+   package com.alibaba.dubbo.samples.api;
+   import java.util.concurrent.CompletableFuture;
+   
@javax.annotation.Generated("com.alibaba.dubbo.async.processor.AsyncAnnotationProcessor")
+   
@org.apache.dubbo.common.config.AsyncFor(com.alibaba.dubbo.samples.api.GreetingsService.class)
+   public interface GreetingsServiceAsync extends GreetingsService {
+   CompletableFuture<java.lang.String> sayHiAsync(java.lang.String name);
+   }
+   ```
+
+   
+
+   
+
+2. Provider端
+
+   
+
+   - 配置
+
+   
+
+   ```
+   <bean id="greetingsService" 
class="com.alibaba.dubbo.samples.async.impl.GreetingsServiceImpl"/>
+   <dubbo:service interface="com.alibaba.dubbo.samples.api.GreetingsService" 
ref="greetingsService"/>
+   ```
+
+   
+
+   - 服务实现
+
+   
+
+   ```
+   public class GreetingsServiceImpl implements GreetingsService {
+       @Override
+       public String sayHi(String name) {
+           return "hi, " + name;
+       }
+   }
+   ```
+
+3. Consumer端
+
+   
+
+   - 配置
+
+   
+
+   ```
+    <dubbo:reference id="greetingsService" 
interface="com.alibaba.dubbo.samples.api.GreetingsServiceAsync"/>
+   ```
+
+   
+
+   注意,服务接口用的是**GreetingsServiceAsync**
+
+   
+
+   - 调用服务
+
+   
+
+   ```
+    public static void main(String[] args) throws Exception {
+           ClassPathXmlApplicationContext context = new 
ClassPathXmlApplicationContext(new 
String[]{"META-INF/spring/async-consumer.xml"});
+           context.start();
+   
+           GreetingsServiceAsync greetingsService = (GreetingsServiceAsync) 
context.getBean("greetingsService");
+           CompletableFuture<String> future = 
greetingsService.sayHiAsync("async call reqeust");
+           System.out.println("async call ret :" + future.get());
+        
+           System.in.read();
+       }
+   ```
+
+   
+
+   这样,我们就可以直接使用`CompletableFuture<String> future = 
greetingsService.sayHiAsync("async call reqeust");`,直接返回CompletableFuture。
+
+## 示例3:使用AsyncContext
+
+本示例演示了如何在同步接口的基础上,通过AsyncContext实现Provider端异步执行,示例代码参见[dubbo-samples-async-provider](https://github.com/dubbo/dubbo-samples/tree/samples-for-2.7.0-SNAPSHOT/dubbo-samples-async-provider)。
+
+1. 定义接口
+
+   
+
+   ```
+   public interface AsyncService {
+       String sayHello(String name);
+   }
+   ```
+
+   
+
+   
+
+2. Provider端
+
+   
+
+   - 配置
+
+   
+
+   ```
+   <bean id="asyncService" 
class="com.alibaba.dubbo.samples.async.impl.AsyncServiceImpl"/>
+   <dubbo:service async="true" 
interface="com.alibaba.dubbo.samples.async.api.AsyncService" 
ref="asyncService"/>
+   ```
+
+   
+
+   注意,要加上`async="true"`表明这是一个开启Provider端异步执行的服务。
+
+   
+
+   - 异步执行实现
+
+   
+
+   ```
+   public class AsyncServiceImpl implements AsyncService {
+       public String sayHello(String name) {
+           final AsyncContext asyncContext = RpcContext.startAsync();
+           new Thread(() -> {
+               asyncContext.signalContextSwitch();
+               try {
+                   Thread.sleep(500);
+               } catch (InterruptedException e) {
+                   e.printStackTrace();
+               }
+               asyncContext.write("Hello " + name + ", response from 
provider.");
+           }).start();
+           return null;
+       }
+   }
+   ```
+
+3. Consumer端
+
+   
+
+   - 配置
+
+   
+
+   ```
+   <dubbo:reference id="asyncService" 
interface="com.alibaba.dubbo.samples.async.api.AsyncService"/>
+   ```
+
+   
+
+   - 服务调用
+
+   
+
+   ```
+    public static void main(String[] args) throws Exception {
+           ClassPathXmlApplicationContext context = new 
ClassPathXmlApplicationContext(new 
String[]{"META-INF/spring/async-consumer.xml"});
+           context.start();
+   
+           AsyncService asyncService = (AsyncService) 
context.getBean("asyncService");
+           System.out.println(asyncService.sayHello("async call request"));
+        
+           System.in.read();
+       }
+   ```
+
+   
+
+   
+
+## 异步引入的新问题
+
+### Filter链
+
+以下是一次普通Dubbo调用的完整Filter链
+
+而采用异步调用后,由于异步结果在异步线程中单独执行,所以流经后半段Filter链的Result是空值,当真正的结果返回时已无法被Filter链处理。
+
+为了解决这个问题,2.7.0中引入了PostProcessFilter和AbstractPostProcessFilter,其中,PostProcessFilter接口继承自Filter接口,AbstractPostProcessFilter是PostProcessFilter的抽象实现。
+
+以下是一个扩展Filter并支持异步Filter链的例子
+
+```
+@Activate(group = {Constants.PROVIDER, Constants.CONSUMER})
+public class AsyncPostprocessFilter extends AbstractPostProcessFilter {
+
+    @Override
+    public Result invoke(Invoker<?> invoker, Invocation invocation) throws 
RpcException {
+        return postProcessResult(invoker.invoke(invocation), invoker, 
invocation);
+    }
+
+    @Override
+    protected Result doPostProcess(Result result, Invoker<?> invoker, 
Invocation invocation) {
+        System.out.println("Filter get the return value: " + 
result.getValue());
+        return result;
+    }
+}
+```
+
+### 上下文传递
+
+当前我们考虑的上下文主要是指保存在RpcContext中的数据,大多数场景是需要用户在切换业务线程前自己完成Context的传递。
+
+```
+public class AsyncServiceImpl implements AsyncService {
+    // 保存当前线程的上下文
+    RpcContext context = RpcContext.getContext();
+    public CompletableFuture<String> sayHello(String name) {
+        return CompletableFuture.supplyAsync(() -> {
+            // 设置到新线程中
+            RpcContext.setContext(context);
+            try {
+                Thread.sleep(5000);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+            return "async response from provider.";
+        });
+    }
+}
+```
+
+不过AsyncContext也提供了signalContextSwitch()的方法来实现方便的Context切换。
+
+```
+public class AsyncServiceImpl implements AsyncService {
+    public String sayHello(String name) {
+        final AsyncContext asyncContext = RpcContext.startAsync();
+        new Thread(() -> {
+            asyncContext.signalContextSwitch();
+            try {
+                Thread.sleep(500);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+            asyncContext.write("Hello " + name + ", response from provider.");
+        }).start();
+        return null;
+    }
+}
+```
+
diff --git a/build/0d69326d69fd47b9d99a.js b/build/0d69326d69fd47b9d99a.js
deleted file mode 100644
index 30ab02d..0000000
--- a/build/0d69326d69fd47b9d99a.js
+++ /dev/null
@@ -1,6 +0,0 @@
-webpackJsonp([1],[,,,,,,,,function(s,n,e){"use strict";function a(s){return 
s&&s.__esModule?s:{default:s}}function t(s,n){if(!(s instanceof n))throw new 
TypeError("Cannot call a class as a function")}function o(s,n){if(!s)throw new 
ReferenceError("this hasn't been initialised - super() hasn't been 
called");return!n||"object"!=typeof n&&"function"!=typeof n?s:n}function 
l(s,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression 
must either be null or a function, not "+ [...]
-  Copyright (c) 2017 Jed Watson.
-  Licensed under the MIT License (MIT), see
-  http://jedwatson.github.io/classnames
-*/
-!function(){"use strict";function e(){for(var 
s=[],n=0;n<arguments.length;n++){var a=arguments[n];if(a){var t=typeof 
a;if("string"===t||"number"===t)s.push(a);else 
if(Array.isArray(a)&&a.length){var l=e.apply(null,a);l&&s.push(l)}else 
if("object"===t)for(var p in a)o.call(a,p)&&a[p]&&s.push(p)}}return s.join(" 
")}var o={}.hasOwnProperty;void 
0!==s&&s.exports?(e.default=e,s.exports=e):(a=[],void 0!==(t=function(){return 
e}.apply(n,a))&&(s.exports=t))}()},function(s,n,e){"use strict";funct [...]
\ No newline at end of file
diff --git a/build/364a8f548578cb3c9f5b.js b/build/5dfcc3831ea1057a5367.js
similarity index 78%
rename from build/364a8f548578cb3c9f5b.js
rename to build/5dfcc3831ea1057a5367.js
index e518342..b382589 100644
--- a/build/364a8f548578cb3c9f5b.js
+++ b/build/5dfcc3831ea1057a5367.js
@@ -3,4 +3,4 @@ webpackJsonp([4],[,,,,,,,function(e,t,n){"use strict";function 
r(e){return e&&e.
   Licensed under the MIT License (MIT), see
   http://jedwatson.github.io/classnames
 */
-!function(){"use strict";function n(){for(var 
e=[],t=0;t<arguments.length;t++){var r=arguments[t];if(r){var o=typeof 
r;if("string"===o||"number"===o)e.push(r);else 
if(Array.isArray(r)&&r.length){var a=n.apply(null,r);a&&e.push(a)}else 
if("object"===o)for(var l in r)i.call(r,l)&&r[l]&&e.push(l)}}return e.join(" 
")}var i={}.hasOwnProperty;void 
0!==e&&e.exports?(n.default=n,e.exports=n):(r=[],void 0!==(o=function(){return 
n}.apply(t,r))&&(e.exports=o))}()},function(e,t,n){"use strict";funct [...]
\ No newline at end of file
+!function(){"use strict";function n(){for(var 
e=[],t=0;t<arguments.length;t++){var r=arguments[t];if(r){var o=typeof 
r;if("string"===o||"number"===o)e.push(r);else 
if(Array.isArray(r)&&r.length){var a=n.apply(null,r);a&&e.push(a)}else 
if("object"===o)for(var l in r)i.call(r,l)&&r[l]&&e.push(l)}}return e.join(" 
")}var i={}.hasOwnProperty;void 
0!==e&&e.exports?(n.default=n,e.exports=n):(r=[],void 0!==(o=function(){return 
n}.apply(t,r))&&(e.exports=o))}()},function(e,t,n){"use strict";funct [...]
\ No newline at end of file
diff --git a/build/fca877d6ab8a5dbba7ca.js b/build/fca877d6ab8a5dbba7ca.js
new file mode 100644
index 0000000..e3172aa
--- /dev/null
+++ b/build/fca877d6ab8a5dbba7ca.js
@@ -0,0 +1,6 @@
+webpackJsonp([1],[,,,,,,,,function(n,s,e){"use strict";function a(n){return 
n&&n.__esModule?n:{default:n}}function t(n,s){if(!(n instanceof s))throw new 
TypeError("Cannot call a class as a function")}function o(n,s){if(!n)throw new 
ReferenceError("this hasn't been initialised - super() hasn't been 
called");return!s||"object"!=typeof s&&"function"!=typeof s?n:s}function 
l(n,s){if("function"!=typeof s&&null!==s)throw new TypeError("Super expression 
must either be null or a function, not "+ [...]
+  Copyright (c) 2017 Jed Watson.
+  Licensed under the MIT License (MIT), see
+  http://jedwatson.github.io/classnames
+*/
+!function(){"use strict";function e(){for(var 
n=[],s=0;s<arguments.length;s++){var a=arguments[s];if(a){var t=typeof 
a;if("string"===t||"number"===t)n.push(a);else 
if(Array.isArray(a)&&a.length){var l=e.apply(null,a);l&&n.push(l)}else 
if("object"===t)for(var r in a)o.call(a,r)&&a[r]&&n.push(r)}}return n.join(" 
")}var o={}.hasOwnProperty;void 
0!==n&&n.exports?(e.default=e,n.exports=e):(a=[],void 0!==(t=function(){return 
e}.apply(s,a))&&(n.exports=t))}()},function(n,s,e){"use strict";funct [...]
\ No newline at end of file
diff --git a/build/page.js b/build/page.js
index ddef2f1..0bb1b81 100644
--- a/build/page.js
+++ b/build/page.js
@@ -1 +1 @@
-!function(n){function o(t){if(e[t])return e[t].exports;var 
r=e[t]={i:t,l:!1,exports:{}};return 
n[t].call(r.exports,r,r.exports,o),r.l=!0,r.exports}var 
t=window.webpackJsonp;window.webpackJsonp=function(o,e,a){for(var 
i,d,l=0,c=[];l<o.length;l++)d=o[l],r[d]&&c.push(r[d][0]),r[d]=0;for(i in 
e)Object.prototype.hasOwnProperty.call(e,i)&&(n[i]=e[i]);for(t&&t(o,e,a);c.length;)c.shift()()};var
 e={},r={5:0};o.e=function(n){function 
t(){d.onerror=d.onload=null,clearTimeout(l);var o=r[n];0!==o&&(o [...]
\ No newline at end of file
+!function(n){function o(t){if(e[t])return e[t].exports;var 
r=e[t]={i:t,l:!1,exports:{}};return 
n[t].call(r.exports,r,r.exports,o),r.l=!0,r.exports}var 
t=window.webpackJsonp;window.webpackJsonp=function(o,e,a){for(var 
i,d,l=0,c=[];l<o.length;l++)d=o[l],r[d]&&c.push(r[d][0]),r[d]=0;for(i in 
e)Object.prototype.hasOwnProperty.call(e,i)&&(n[i]=e[i]);for(t&&t(o,e,a);c.length;)c.shift()()};var
 e={},r={5:0};o.e=function(n){function 
t(){d.onerror=d.onload=null,clearTimeout(l);var o=r[n];0!==o&&(o [...]
\ No newline at end of file
diff --git a/md_json/blog.json b/md_json/blog.json
index 330222f..40ba1b7 100644
--- a/md_json/blog.json
+++ b/md_json/blog.json
@@ -26,7 +26,7 @@
     },
     {
       "filename": "dubbo-loadbalance.md",
-      "__html": ""
+      "__html": 
"<h1>Dubbo的负载均衡</h1>\n<h1>背景</h1>\n<p>Dubbo是一个分布式服务框架,能避免单点故障和支持服务的横向扩容。一个服务通常会部署多个实例。如何从多个服务提供者组成的集群中挑选出一个进行调用,就涉及到一个负载均衡的策略。</p>\n<h1>几个概念</h1>\n<p>在讨论负载均衡之前,我想先解释一下这3个概念。</p>\n<ol>\n<li>负载均衡</li>\n<li>集群容错</li>\n<li>服务路由</li>\n</ol>\n<p>这3个概念容易混淆。他们都描述了怎么从多个Provider中选择一个来进行调用。那他们到底有什么区别呢?下面我来举一个简单的例子,把这几个概念阐述清楚吧。</p>\n<p>有一个Dubbo的用户服务,在北京部署了10个,在上海部署了20个。一个杭州的服务消费方发起了一次调用,然后发生了以下的事情:</p>\n<ol>\n<li>根据配置的路由规则,如果杭州发起的调用,会路由到比较�
 �的上海的20个Provider。</li>\n<li>根据配置的随机负载均衡策略。在20 [...]
     },
     {
       "filename": "dubbo-meetup-shanghai-jun-23rd-2018.md",
@@ -34,7 +34,7 @@
     },
     {
       "filename": "dubbo-new-async.md",
-      "__html": ""
+      "__html": 
"<h1>如何基于Dubbo实现全异步调用链</h1>\n<p>基于Dubbo实现全异步编程,是在2.7.0版本中对现有异步方式增强后新引入的功能。本文先是回顾2.6.x及之前版本对异步的支持情况及存在的问题,引出了2.7.0版本基于CompletableFuture做了哪些针对性的增强,通过几个示例详细阐述了增强后的异步编程的使用方式,最后总结了引入异步模式带来的新问题及Dubbo的解决方法。通过阅读这篇文章,可以很容易的基于Dubbo2.7.0+版本实现一个全异步的远程服务调用链路。</p>\n<h2>2.6.x版本之前的异步方式</h2>\n<p>在2.6.x及之前的版本提供了一定的异步编程能力,包括Consumer端<a
 
href=\"http://dubbo.apache.org/books/dubbo-user-book/demos/async-call.html\";>异步调用</a>、<a
 href=\"http://dubbo.apache.org/books/dubbo-user-book/demos/callback- [...]
     },
     {
       "filename": "dubbo-zk.md",
diff --git a/site_config/blog.js b/site_config/blog.js
index 5603eba..9ee1931 100644
--- a/site_config/blog.js
+++ b/site_config/blog.js
@@ -91,7 +91,7 @@ export default {
             author: '@leiwei',
             dateStr: 'August 10th, 2018',
             desc: '本文介绍了负载均衡的相关概念以及 Dubbo 中的负载均衡策略实现',
-            link: '/blog/dubbo-generic-invoke.md',
+            link: '/blog/dubbo-loadbalance.md',
         },
         {
             title: 'Dubbo的泛化调用',

Reply via email to