You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by il...@apache.org on 2019/04/09 06:51:10 UTC

[incubator-dubbo-website] branch asf-site updated: add blog for dubbo stub and dubbo mock, and rebuild

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

iluo 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 36219a7  add blog for dubbo stub and dubbo mock, and rebuild
36219a7 is described below

commit 36219a7756ada5130845da074a1d29980ccdde8d
Author: Ian Luo <ia...@gmail.com>
AuthorDate: Tue Apr 9 14:50:40 2019 +0800

    add blog for dubbo stub and dubbo mock, and rebuild
---
 blog/zh-cn/dubbo-stub-mock.md     | 313 ++++++++++++++++++++++++++++++++++++++
 build/blog.js                     |   4 +-
 en-us/blog/download.html          |   9 ++
 en-us/blog/download.json          |   2 +-
 img/blog/dubbo-mock-stub-flow.png | Bin 0 -> 20020 bytes
 md_json/blog.json                 |   5 +
 site_config/blog.js               |   7 +
 zh-cn/blog/download.html          |   9 ++
 zh-cn/blog/download.json          |   2 +-
 zh-cn/blog/dubbo-stub-mock.html   | 238 +++++++++++++++++++++++++++++
 zh-cn/blog/dubbo-stub-mock.json   |   6 +
 zh-cn/blog/index.html             |   2 +-
 12 files changed, 592 insertions(+), 5 deletions(-)

diff --git a/blog/zh-cn/dubbo-stub-mock.md b/blog/zh-cn/dubbo-stub-mock.md
new file mode 100644
index 0000000..fa7a3af
--- /dev/null
+++ b/blog/zh-cn/dubbo-stub-mock.md
@@ -0,0 +1,313 @@
+# 本地存根和本地伪装
+
+## 基本概念
+
+典型的 RPC 调用客户端是依赖并且只依赖接口编程来进行远程调用的。在真正发起远程调用之前,用户往往需要做一些预处理的工作,比如提前校验参数。在拿到返回调用结果之后,用户可能需要缓存结果,或者是在调用失败的时候构造容错数据,而不是简单的抛出异常。
+
+
+
+这个时候,用户可以编写出类似以下的代码来处理上面提出的这些场景:
+
+```java
+try {
+    preProcess();
+    return service.invoke(...);
+} catch (Throwable e) {
+    return mockValue;
+} finally {
+    postProcess();
+}
+```
+
+
+
+类似的,用户也可以通过面向切面编程 *AOP* 的高级技巧来解决上面的诉求,比如通过 *Spring AOP* 的方式可以通过类似下面的这段配置来完成。使用 *AOP* 的技巧相比上面的代码来说,避免了容错处理等与业务无关的代码对业务代码的侵入,使得业务处理主逻辑更简洁。
+
+```xml
+<bean id="demo-service-stub" class="org.apache.dubbo.demo.DemoServiceStub"/>
+<bean id="demo-service-mock" class="org.apache.dubbo.demo.DemoServiceMock"/>
+<aop:config>
+    <aop:aspect id="stub" ref="demo-service-stub">
+        <aop:pointcut id="stubPointcut" expression="execution(* org.apache.dubbo.samples.DemoService+.*(..))"/>
+        <aop:before method="preProcess" pointcut-ref="stubPointcut"/>
+        <aop:after-returning method="postProcess" pointcut-ref="stubPointcut"/>
+    </aop:aspect>
+    <aop:aspect id="mock" ref="demo-service-mock">
+        <aop:pointcut id="mockPointcut" expression="execution(* org.apache.dubbo.samples.DemoService+.*(..))"/>
+        <aop:after-throwing method="mock" pointcut-ref="mockPointcut"/>
+    </aop:aspect>
+</aop:config>
+```
+
+
+
+为了进一步的方便用户做 Dubbo 开发,框架提出了本地存根 *Stub* 和本地伪装 *Mock* 的概念。通过约定大于配置的理念,进一步的简化了配置,使用起来更加方便,并且不依赖额外的 *AOP* 框架就达到了 *AOP* 的效果。
+
+
+
+本地存根的工作方式与 *AOP* 的 **around** advice 类似,而本地伪装的工作方式等同于 *AOP* 中的 **after-throwing** advice,也就是说,只有当远程调用发生 *exception* 的时候才会执行本地伪装。本地存根和本地伪装的工作流程如下图所示:
+
+![dubbo-mock-stub-flow](../../img/blog/dubbo-mock-stub-flow.png)
+
+
+1. 服务消费者发起调用
+2. 如果服务消费者端存在本地存根 *Stub* 的话,会先执行本地存根
+3. 本地存根 Stub 持有远程服务的 *Proxy* 对象,*Stub* 在执行的时候,会先执行自己的逻辑 (*before*),然后通过 *Proxy* 发起远程调用,最后在返回过程之前也会执行自己的逻辑 (*after-returning*)
+4. 如果远程服务的 *Proxy* 对象在执行过程中抛出了 *exception*,会执行服务消费端的本地伪装 *Mock* 的逻辑 (*after-throwing*),返回容错数据,从而达到服务降级的目的
+
+
+
+## 开发一个本地存根 Stub
+
+本地存根 *Stub* 由用户来提供,并在服务消费方部署。完整的示例可以在这里 [^stub-samples] 获得。
+
+
+
+```java
+public class DemoServiceStub implements DemoService { // #1
+    private static Logger logger = LoggerFactory.getLogger(DemoServiceStub.class);
+
+    private final DemoService demoService;
+
+    public DemoServiceStub(DemoService demoService) { // #2
+        this.demoService = demoService;
+    }
+
+    @Override
+    public String sayHello(String name) { // #3
+        logger.info("before execute remote service, parameter: " + name); // #4
+        try {
+            String result = demoService.sayHello(name); // #5
+            logger.info("after execute remote service, result: " + result); // #6
+            return result;
+        } catch (Exception e) {
+            logger.warn("fail to execute service", e); // #7
+            return null;
+        }
+    }
+}
+```
+
+
+
+要和框架在一起工作,本地存根的实现需要遵循一些与框架事先做出的约定:
+
+1. 首先本地存根 *Stub* 是服务接口的一个实现
+2. 本地存根的实现需要提供一个拷贝构造方法,方便框架将远程调用的 *Proxy* 对象注入进来
+3. 同样的,本地存根需要提供服务接口中所有方法的实现。在本例中,需要实现 *sayHello* 方法
+4. 在真正发起远程调用之前,用户可以在本地执行一些操作。在本例中,在日志中记录传入的参数
+5. 通过框架传入的 *Proxy* 对象真正发起远程调用
+6. 在远程调用结束后,也可以加入本地代码的执行。在本例中,在日志中记录远程调用的返回结果
+7. 如果发生错误的时候,也可以做一些错误恢复的动作。在本例中,在日志中记录异常。当然,如果提供了本地伪装的话,*catch* 中的逻辑是可以省略掉的
+
+其中步骤 4、步骤 6、和步骤 7 共同构建了等同于面向切面编程中的概念,分别对应于 **before**、**after-returning**、以及 **after-throwing**。
+
+
+
+*DemoServiceStub* 运行在客户端,要使用本地存根的话,还需要在 *stub-consumer.xml* 中配置属性 *stub*。可以简单的通过指定 *stub="true"* 来告诉 Dubbo 框架使用本地存根,这个时候,本地存根的包名需要和服务接口的包名一致,类名必须在服务接口的类名后加上 **Stub** 的后缀。例如,当服务接口名是 *org.apache.dubbo.samples.stub.api.DemoService* 时,本地存根的全类名应该是 *org.apache.dubbo.samples.stub.api.DemoServiceStub*。
+
+
+
+```xml
+<dubbo:reference id="demoService" check="false" interface="org.apache.dubbo.samples.stub.api.DemoService" stub="true"/>
+```
+
+
+
+如果不希望使用默认的命名规则,也可以直接通过 *stub* 属性来指定本地存根的全类名。
+
+
+
+```xml
+<dubbo:reference id="demoService" check="false" interface="org.apache.dubbo.samples.stub.api.DemoService" stub="org.apache.dubbo.samples.stub.impl.DemoStub"/>
+```
+
+
+
+启动服务端 *StubProvider* 后,再运行客户端 *StubConsumer*,可以通过观察客户端的日志来验证本地存根的运行结果。
+
+
+
+```bash
+[09/04/19 11:52:21:021 CST] main  INFO api.DemoServiceStub: before execute remote service, parameter: dubbo
+[09/04/19 11:52:21:021 CST] main  INFO api.DemoServiceStub: after execute remote service, result: greeting dubbo
+[09/04/19 11:52:21:021 CST] main  INFO stub.StubConsumer: result: greeting dubbo
+```
+
+
+
+## 开发一个本地伪装 Mock
+
+上面说了本地伪装通常用于在远程调用出错的情况下服务降级。完整的示例可以在这里 [^mock-samples] 获得。
+
+
+
+这里通过在服务提供方的代码中睡眠来模拟调用端超时,从而执行本地伪装来做容错处理。
+
+```java
+public class DemoServiceImpl implements DemoService {
+    public String sayHello(String name) {
+        try {
+            Thread.sleep(5000); // #1
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        return "hello " + name; // #2
+    }
+}
+```
+
+1. Dubbo 默认的超时时间是 *1000 ms*,这里通过睡眠 *5000ms* 来达到触发超时异常的发生
+2. 由于超时的发生,这个结果并不会被返回给客户端,取而代之的是 *org.apache.dubbo.remoting.TimeoutException*
+
+
+
+在客户端提供本地伪装的实现。当远程调用发生错误的时候,返回给调用方的不是服务端的 "hello name",取而代之的是 "mock name"。
+
+
+
+```java
+public class DemoServiceMock implements DemoService {
+    private static Logger logger = LoggerFactory.getLogger(DemoServiceMock.class);
+
+    public String sayHello(String name) {
+        logger.warn("about to execute mock: " + DemoServiceMock.class.getSimpleName());
+        return "mock " + name;
+    }
+}
+```
+
+
+
+同样的,要使用本地伪装的话,还需要在 *mock-consumer.xml* 中配置属性 *mock*。可以简单的通过指定 *mock="true"* 来告诉 Dubbo 框架使用本地伪装,这个时候,本地伪装的包名需要和服务接口的包名一致,类名必须在服务接口的类名后加上 **Mock** 的后缀。例如,当服务接口名是 *org.apache.dubbo.samples.stub.api.DemoService* 时,本地存根的全类名应该是 *org.apache.dubbo.samples.stub.api.DemoServiceMock*。
+
+
+
+```xml
+    <dubbo:reference id="demoService" check="false" interface="org.apache.dubbo.samples.mock.api.DemoService"
+                     mock="true"/>
+```
+
+
+
+如果不希望使用默认的命名规则,也可以直接通过 *mock* 属性来指定本地伪装的全类名。
+
+
+
+```xml
+<dubbo:reference id="demoService" check="false" interface="org.apache.dubbo.samples.mock.api.DemoService" stub="org.apache.dubbo.samples.mock.impl.DemoMock"/>
+```
+
+
+
+通过提供一个本地伪装的类,可以最大限度的控制出错之后的容错逻辑。有的时候,业务上并不需要这样灵活的机制,只有返回一个默认值的诉求,这个时候提供一个完整的本地伪装的实现就显得有点重了。或者线上出错的时候,应用并没有打包本地伪装,需要通过推送规则的方式临时对服务降级。Dubbo 框架为上面的这两种诉求都提供了快捷方式,帮助用户快速配置服务降级。
+
+
+
+启动服务端 *MockProvider* 之后,然后再执行 *MockConsumer* 就可以看到下面的结果:
+
+
+
+```bash
+Caused by: org.apache.dubbo.remoting.TimeoutException: Waiting server-side response timeout by scan timer. start time: 2019-04-09 14:20:48.061, end time: 2019-04-09 14:20:49.077, client elapsed: 0 ms, server elapsed: 1015 ms, timeout: 1000 ms, request: Request [id=2, version=2.0.2, twoway=true, event=false, broken=false, data=RpcInvocation [methodName=sayHello, parameterTypes=[class java.lang.String], arguments=[world], attachments={path=org.apache.dubbo.samples.mock.api.DemoService, int [...]
+	at org.apache.dubbo.remoting.exchange.support.DefaultFuture.returnFromResponse(DefaultFuture.java:295)
+	at org.apache.dubbo.remoting.exchange.support.DefaultFuture.get(DefaultFuture.java:191)
+	at org.apache.dubbo.remoting.exchange.support.DefaultFuture.get(DefaultFuture.java:164)
+	at org.apache.dubbo.rpc.protocol.dubbo.DubboInvoker.doInvoke(DubboInvoker.java:108)
+	at org.apache.dubbo.rpc.protocol.AbstractInvoker.invoke(AbstractInvoker.java:157)
+	at org.apache.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:88)
+	at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:73)
+	at org.apache.dubbo.rpc.protocol.dubbo.filter.FutureFilter.invoke(FutureFilter.java:49)
+	at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:73)
+	at org.apache.dubbo.rpc.filter.ConsumerContextFilter.invoke(ConsumerContextFilter.java:54)
+	at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:73)
+	at org.apache.dubbo.rpc.listener.ListenerInvokerWrapper.invoke(ListenerInvokerWrapper.java:78)
+	at org.apache.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:56)
+	at org.apache.dubbo.rpc.cluster.support.FailoverClusterInvoker.doInvoke(FailoverClusterInvoker.java:80)
+	... 5 more
+[09/04/19 02:20:49:049 CST] main  WARN api.DemoServiceMock: about to execute mock: DemoServiceMock
+[09/04/19 02:20:49:049 CST] main  INFO mock.MockConsumer: result: mock world
+```
+
+
+
+
+
+下面通过规则推送为例展示这种快捷方式的用法,更多的用法请参考 Dubbo 官方用户手册 [^mock]。通过向配置中心推送指定服务的配置,就可以做到动态服务降级的目的。
+
+
+
+```yaml
+--- # 1
+configVersion: v2.7
+scope: service
+  key: org.apache.dubbo.samples.mock.api.DemoService #2
+  enabled: true
+  configs:
+    - addresses: [0.0.0.0]
+      side: consumer #3
+      parameters:
+        mock: return configured-mock-value #4
+      ...
+```
+
+1. 以 *Zookeeper* 为例,规则的路径是 /dubbo/config/org.apache.dubbo.samples.mock.api.DemoService/configurators
+2. 该规则作用在 *org.apache.dubbo.samples.mock.api.DemoService* 服务上
+3. 该规则作用在客户端
+4. 当错误发送时,对服务的调用返回默认值 *configured-mock-value*
+
+
+
+启动服务端 *MockProvider* 之后,再执行例子[^mock-samples]中的 *Configurator* 完成对服务降级规则的配置,然后再执行 *MockConsumer* 就可以看到下面的结果:
+
+
+
+```bash
+[09/04/19 02:19:01:001 CST] main  INFO integration.AbstractConfiguratorListener:  [DUBBO] Notification of overriding rule, change type is: MODIFIED, raw config content is:
+ ---
+configVersion: v2.7
+scope: service
+key: org.apache.dubbo.samples.mock.api.DemoService
+enabled: true
+configs:
+- addresses: [0.0.0.0]
+  side: consumer
+  parameters:
+    mock: return configured-mock-value
+...
+, dubbo version: 2.7.1, current host: 30.5.125.99
+
+...
+
+Caused by: org.apache.dubbo.remoting.TimeoutException: Waiting server-side response timeout. start time: 2019-04-09 14:19:03.737, end time: 2019-04-09 14:19:04.741, client elapsed: 1 ms, server elapsed: 1002 ms, timeout: 1000 ms, request: Request [id=2, version=2.0.2, twoway=true, event=false, broken=false, data=RpcInvocation [methodName=sayHello, parameterTypes=[class java.lang.String], arguments=[world], attachments={path=org.apache.dubbo.samples.mock.api.DemoService, interface=org.apa [...]
+	at org.apache.dubbo.remoting.exchange.support.DefaultFuture.get(DefaultFuture.java:188)
+	at org.apache.dubbo.remoting.exchange.support.DefaultFuture.get(DefaultFuture.java:164)
+	at org.apache.dubbo.rpc.protocol.dubbo.DubboInvoker.doInvoke(DubboInvoker.java:108)
+	at org.apache.dubbo.rpc.protocol.AbstractInvoker.invoke(AbstractInvoker.java:157)
+	at org.apache.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:88)
+	at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:73)
+	at org.apache.dubbo.rpc.protocol.dubbo.filter.FutureFilter.invoke(FutureFilter.java:49)
+	at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:73)
+	at org.apache.dubbo.rpc.filter.ConsumerContextFilter.invoke(ConsumerContextFilter.java:54)
+	at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:73)
+	at org.apache.dubbo.rpc.listener.ListenerInvokerWrapper.invoke(ListenerInvokerWrapper.java:78)
+	at org.apache.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:56)
+	at org.apache.dubbo.rpc.cluster.support.FailoverClusterInvoker.doInvoke(FailoverClusterInvoker.java:80)
+	... 5 more
+[09/04/19 02:19:04:004 CST] main  INFO mock.MockConsumer: result: configured-mock-value
+```
+
+
+
+## 总结
+
+本文介绍了 Dubbo 中本地存根和本地伪装的概念和用法。从本质来讲,本地存根和本地伪装等同于面向切面编程中的概念,通过诸如 Spring 框架提供的 *AOP* 编程可以达到同样的目的。通过本文如何开发一个本地存根和本地伪装的示例,读者可以直观的感受到通过框架提供的机制更加方便快捷。同时,对于很多简单的场景和动态配置推送的场景,框架提供了仅通过配置而无需编码的方式来满足,进一步提升了框架使用者的效率。 
+
+
+
+[^stub-samples]: https://github.com/apache/incubator-dubbo-samples/tree/master/dubbo-samples-stub
+[^mock-samples]:  https://github.com/apache/incubator-dubbo-samples/tree/master/dubbo-samples-mock
+[^mock]: http://dubbo.apache.org/zh-cn/docs/user/demos/local-mock.html
+
+
+
diff --git a/build/blog.js b/build/blog.js
index 6878fce..6e90671 100644
--- a/build/blog.js
+++ b/build/blog.js
@@ -1,6 +1,6 @@
-!function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="/build/",t(t.s=78) [...]
+!function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="/build/",t(t.s=78) [...]
   Copyright (c) 2017 Jed Watson.
   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 u in r)i.call(r,u)&&r[u]&&e.push(u)}}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 u in r)i.call(r,u)&&r[u]&&e.push(u)}}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/en-us/blog/download.html b/en-us/blog/download.html
index 0b451df..22c3590 100644
--- a/en-us/blog/download.html
+++ b/en-us/blog/download.html
@@ -86,6 +86,15 @@
 <blockquote>
 <p>Release Notes: <a href="https://github.com/apache/incubator-dubbo-spring-boot-project/releases">https://github.com/apache/incubator-dubbo-spring-boot-project/releases</a></p>
 </blockquote>
+<h3>2.7.1 (2019-04-09)</h3>
+<ul>
+<li><a href="https://www.apache.org/dyn/closer.cgi?path=incubator/dubbo/spring-boot-project/2.7.1/apache-dubbo-spring-boot-project-incubating-2.7.1-source-release.zip">source</a> |
+<a href="https://dist.apache.org/repos/dist/release/incubator/dubbo/spring-boot-project/2.7.1/apache-dubbo-spring-boot-project-incubating-2.7.1-source-release.zip.asc">asc</a> |
+<a href="https://dist.apache.org/repos/dist/release/incubator/dubbo/spring-boot-project/2.7.1/apache-dubbo-spring-boot-project-incubating-2.7.1-source-release.zip.sha512">sha512</a></li>
+<li><a href="https://www.apache.org/dyn/closer.cgi?path=incubator/dubbo/spring-boot-project/2.7.1/apache-dubbo-spring-boot-project-incubating-2.7.1-bin-release.zip">binary</a> |
+<a href="https://dist.apache.org/repos/dist/release/incubator/dubbo/spring-boot-project/2.7.1/apache-dubbo-spring-boot-project-incubating-2.7.1-bin-release.zip.asc">asc</a> |
+<a href="https://dist.apache.org/repos/dist/release/incubator/dubbo/spring-boot-project/2.7.1/apache-dubbo-spring-boot-project-incubating-2.7.1-bin-release.zip.sha512">sha512</a></li>
+</ul>
 <h3>2.7.0 (2019-02-14)</h3>
 <ul>
 <li><a href="https://www.apache.org/dyn/closer.cgi?path=incubator/dubbo/spring-boot-project/2.7.0/apache-dubbo-spring-boot-project-incubating-2.7.0-source-release.zip">source</a> |
diff --git a/en-us/blog/download.json b/en-us/blog/download.json
index e60d8aa..c204c66 100644
--- a/en-us/blog/download.json
+++ b/en-us/blog/download.json
@@ -1,6 +1,6 @@
 {
   "filename": "download.md",
-  "__html": "<h1>Downloads</h1>\n<h2>Verification</h2>\n<p>you can follow these <a href=\"https://www.apache.org/info/verification\">procedures</a> and the <a href=\"https://dist.apache.org/repos/dist/release/incubator/dubbo/KEYS\">KEYS</a> file to verify the download files</p>\n<h2><a href=\"https://github.com/apache/incubator-dubbo\">Dubbo-RPC</a></h2>\n<blockquote>\n<p>Release Notes: <a href=\"https://github.com/apache/incubator-dubbo/releases\">https://github.com/apache/incubator-dub [...]
+  "__html": "<h1>Downloads</h1>\n<h2>Verification</h2>\n<p>you can follow these <a href=\"https://www.apache.org/info/verification\">procedures</a> and the <a href=\"https://dist.apache.org/repos/dist/release/incubator/dubbo/KEYS\">KEYS</a> file to verify the download files</p>\n<h2><a href=\"https://github.com/apache/incubator-dubbo\">Dubbo-RPC</a></h2>\n<blockquote>\n<p>Release Notes: <a href=\"https://github.com/apache/incubator-dubbo/releases\">https://github.com/apache/incubator-dub [...]
   "link": "/en-us/blog/download.html",
   "meta": {}
 }
\ No newline at end of file
diff --git a/img/blog/dubbo-mock-stub-flow.png b/img/blog/dubbo-mock-stub-flow.png
new file mode 100644
index 0000000..3d6c0e3
Binary files /dev/null and b/img/blog/dubbo-mock-stub-flow.png differ
diff --git a/md_json/blog.json b/md_json/blog.json
index c62baef..3b8158f 100644
--- a/md_json/blog.json
+++ b/md_json/blog.json
@@ -531,6 +531,11 @@
       "meta": {}
     },
     {
+      "filename": "dubbo-stub-mock.md",
+      "link": "/zh-cn/blog/dubbo-stub-mock.html",
+      "meta": {}
+    },
+    {
       "filename": "dubbo-zk.md",
       "link": "/zh-cn/blog/dubbo-zk.html",
       "meta": {
diff --git a/site_config/blog.js b/site_config/blog.js
index 06371e8..0355aea 100644
--- a/site_config/blog.js
+++ b/site_config/blog.js
@@ -164,6 +164,13 @@ export default {
         postsTitle: '所有文章',
         list: [
             {
+                title: 'Dubbo 本地存根和本地伪装',
+                author: '@beiwei30',
+                dateStr: 'Apr 9th, 2019',
+                desc: '介绍本地伪装、本地存根的概念以及如何开发',
+                link: '/zh-cn/blog/dubbo-stub-mock.html',
+            },
+            {
                 title: 'Dubbo2.7 三大新特性详解',
                 author: '@lexburner',
                 dateStr: 'Mar 22nd, 2019',
diff --git a/zh-cn/blog/download.html b/zh-cn/blog/download.html
index 281c7aa..83d1996 100644
--- a/zh-cn/blog/download.html
+++ b/zh-cn/blog/download.html
@@ -87,6 +87,15 @@
 <blockquote>
 <p>发布说明:<a href="https://github.com/apache/incubator-dubbo-spring-boot-project/releases">https://github.com/apache/incubator-dubbo-spring-boot-project/releases</a></p>
 </blockquote>
+<h3>2.7.1 (2019-04-09)</h3>
+<ul>
+<li><a href="https://www.apache.org/dyn/closer.cgi?path=incubator/dubbo/spring-boot-project/2.7.1/apache-dubbo-spring-boot-project-incubating-2.7.1-source-release.zip">source</a> |
+<a href="https://dist.apache.org/repos/dist/release/incubator/dubbo/spring-boot-project/2.7.1/apache-dubbo-spring-boot-project-incubating-2.7.1-source-release.zip.asc">asc</a> |
+<a href="https://dist.apache.org/repos/dist/release/incubator/dubbo/spring-boot-project/2.7.1/apache-dubbo-spring-boot-project-incubating-2.7.1-source-release.zip.sha512">sha512</a></li>
+<li><a href="https://www.apache.org/dyn/closer.cgi?path=incubator/dubbo/spring-boot-project/2.7.1/apache-dubbo-spring-boot-project-incubating-2.7.1-bin-release.zip">binary</a> |
+<a href="https://dist.apache.org/repos/dist/release/incubator/dubbo/spring-boot-project/2.7.1/apache-dubbo-spring-boot-project-incubating-2.7.1-bin-release.zip.asc">asc</a> |
+<a href="https://dist.apache.org/repos/dist/release/incubator/dubbo/spring-boot-project/2.7.1/apache-dubbo-spring-boot-project-incubating-2.7.1-bin-release.zip.sha512">sha512</a></li>
+</ul>
 <h3>2.7.0 (2019-02-14)</h3>
 <ul>
 <li><a href="https://www.apache.org/dyn/closer.cgi?path=incubator/dubbo/spring-boot-project/2.7.0/apache-dubbo-spring-boot-project-incubating-2.7.0-source-release.zip">source</a> |
diff --git a/zh-cn/blog/download.json b/zh-cn/blog/download.json
index bdd679a..ee15d2c 100644
--- a/zh-cn/blog/download.json
+++ b/zh-cn/blog/download.json
@@ -1,6 +1,6 @@
 {
   "filename": "download.md",
-  "__html": "<h1>下载中心</h1>\n<h2>验证</h2>\n<p>可以按照这里的<a href=\"https://www.apache.org/info/verification\">步骤</a>, 利用<a href=\"https://dist.apache.org/repos/dist/release/incubator/dubbo/KEYS\">KEYS</a>文件来验证下载。</p>\n<h2>版本与升级</h2>\n<p>请点击了解各<a href=\"http://dubbo.apache.org/zh-cn/docs/user/versions/index.html\">版本详情和升级注意事项</a></p>\n<blockquote>\n<p>发布说明:<a href=\"https://github.com/apache/incubator-dubbo/releases\">https://github.com/apache/incubator-dubbo/releases</a></p>\n</blockquote>\n<h [...]
+  "__html": "<h1>下载中心</h1>\n<h2>验证</h2>\n<p>可以按照这里的<a href=\"https://www.apache.org/info/verification\">步骤</a>, 利用<a href=\"https://dist.apache.org/repos/dist/release/incubator/dubbo/KEYS\">KEYS</a>文件来验证下载。</p>\n<h2>版本与升级</h2>\n<p>请点击了解各<a href=\"http://dubbo.apache.org/zh-cn/docs/user/versions/index.html\">版本详情和升级注意事项</a></p>\n<blockquote>\n<p>发布说明:<a href=\"https://github.com/apache/incubator-dubbo/releases\">https://github.com/apache/incubator-dubbo/releases</a></p>\n</blockquote>\n<h [...]
   "link": "/zh-cn/blog/download.html",
   "meta": {}
 }
\ No newline at end of file
diff --git a/zh-cn/blog/dubbo-stub-mock.html b/zh-cn/blog/dubbo-stub-mock.html
new file mode 100644
index 0000000..153bd93
--- /dev/null
+++ b/zh-cn/blog/dubbo-stub-mock.html
@@ -0,0 +1,238 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+	<meta charset="UTF-8">
+	<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
+	<meta name="keywords" content="dubbo-stub-mock" />
+	<meta name="description" content="dubbo-stub-mock" />
+	<!-- 网页标签标题 -->
+	<title>dubbo-stub-mock</title>
+	<link rel="shortcut icon" href="/img/dubbo.ico"/>
+	<link rel="stylesheet" href="/build/blogDetail.css" />
+</head>
+<body>
+	<div id="root"><div class="blog-detail-page" data-reactroot=""><header class="header-container header-container-normal"><div class="header-body"><a href="/zh-cn/index.html"><img class="logo" src="/img/dubbo_colorful.png"/></a><div class="search search-normal"><span class="icon-search"></span></div><span class="language-switch language-switch-normal">En</span><div class="header-menu"><img class="header-menu-toggle" src="/img/menu_gray.png"/><ul><li class="menu-item menu-item-normal"><a h [...]
+<h2>基本概念</h2>
+<p>典型的 RPC 调用客户端是依赖并且只依赖接口编程来进行远程调用的。在真正发起远程调用之前,用户往往需要做一些预处理的工作,比如提前校验参数。在拿到返回调用结果之后,用户可能需要缓存结果,或者是在调用失败的时候构造容错数据,而不是简单的抛出异常。</p>
+<p>这个时候,用户可以编写出类似以下的代码来处理上面提出的这些场景:</p>
+<pre><code class="language-java"><span class="hljs-keyword">try</span> {
+    preProcess();
+    <span class="hljs-keyword">return</span> service.invoke(...);
+} <span class="hljs-keyword">catch</span> (Throwable e) {
+    <span class="hljs-keyword">return</span> mockValue;
+} <span class="hljs-keyword">finally</span> {
+    postProcess();
+}
+</code></pre>
+<p>类似的,用户也可以通过面向切面编程 <em>AOP</em> 的高级技巧来解决上面的诉求,比如通过 <em>Spring AOP</em> 的方式可以通过类似下面的这段配置来完成。使用 <em>AOP</em> 的技巧相比上面的代码来说,避免了容错处理等与业务无关的代码对业务代码的侵入,使得业务处理主逻辑更简洁。</p>
+<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">bean</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"demo-service-stub"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"org.apache.dubbo.demo.DemoServiceStub"</span>/&gt;</span>
+<span class="hljs-tag">&lt;<span class="hljs-name">bean</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"demo-service-mock"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"org.apache.dubbo.demo.DemoServiceMock"</span>/&gt;</span>
+<span class="hljs-tag">&lt;<span class="hljs-name">aop:config</span>&gt;</span>
+    <span class="hljs-tag">&lt;<span class="hljs-name">aop:aspect</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"stub"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"demo-service-stub"</span>&gt;</span>
+        <span class="hljs-tag">&lt;<span class="hljs-name">aop:pointcut</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"stubPointcut"</span> <span class="hljs-attr">expression</span>=<span class="hljs-string">"execution(* org.apache.dubbo.samples.DemoService+.*(..))"</span>/&gt;</span>
+        <span class="hljs-tag">&lt;<span class="hljs-name">aop:before</span> <span class="hljs-attr">method</span>=<span class="hljs-string">"preProcess"</span> <span class="hljs-attr">pointcut-ref</span>=<span class="hljs-string">"stubPointcut"</span>/&gt;</span>
+        <span class="hljs-tag">&lt;<span class="hljs-name">aop:after-returning</span> <span class="hljs-attr">method</span>=<span class="hljs-string">"postProcess"</span> <span class="hljs-attr">pointcut-ref</span>=<span class="hljs-string">"stubPointcut"</span>/&gt;</span>
+    <span class="hljs-tag">&lt;/<span class="hljs-name">aop:aspect</span>&gt;</span>
+    <span class="hljs-tag">&lt;<span class="hljs-name">aop:aspect</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"mock"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"demo-service-mock"</span>&gt;</span>
+        <span class="hljs-tag">&lt;<span class="hljs-name">aop:pointcut</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"mockPointcut"</span> <span class="hljs-attr">expression</span>=<span class="hljs-string">"execution(* org.apache.dubbo.samples.DemoService+.*(..))"</span>/&gt;</span>
+        <span class="hljs-tag">&lt;<span class="hljs-name">aop:after-throwing</span> <span class="hljs-attr">method</span>=<span class="hljs-string">"mock"</span> <span class="hljs-attr">pointcut-ref</span>=<span class="hljs-string">"mockPointcut"</span>/&gt;</span>
+    <span class="hljs-tag">&lt;/<span class="hljs-name">aop:aspect</span>&gt;</span>
+<span class="hljs-tag">&lt;/<span class="hljs-name">aop:config</span>&gt;</span>
+</code></pre>
+<p>为了进一步的方便用户做 Dubbo 开发,框架提出了本地存根 <em>Stub</em> 和本地伪装 <em>Mock</em> 的概念。通过约定大于配置的理念,进一步的简化了配置,使用起来更加方便,并且不依赖额外的 <em>AOP</em> 框架就达到了 <em>AOP</em> 的效果。</p>
+<p>本地存根的工作方式与 <em>AOP</em> 的 <strong>around</strong> advice 类似,而本地伪装的工作方式等同于 <em>AOP</em> 中的 <strong>after-throwing</strong> advice,也就是说,只有当远程调用发生 <em>exception</em> 的时候才会执行本地伪装。本地存根和本地伪装的工作流程如下图所示:</p>
+<p><img src="../../img/blog/dubbo-mock-stub-flow.png" alt="dubbo-mock-stub-flow"></p>
+<ol>
+<li>服务消费者发起调用</li>
+<li>如果服务消费者端存在本地存根 <em>Stub</em> 的话,会先执行本地存根</li>
+<li>本地存根 Stub 持有远程服务的 <em>Proxy</em> 对象,<em>Stub</em> 在执行的时候,会先执行自己的逻辑 (<em>before</em>),然后通过 <em>Proxy</em> 发起远程调用,最后在返回过程之前也会执行自己的逻辑 (<em>after-returning</em>)</li>
+<li>如果远程服务的 <em>Proxy</em> 对象在执行过程中抛出了 <em>exception</em>,会执行服务消费端的本地伪装 <em>Mock</em> 的逻辑 (<em>after-throwing</em>),返回容错数据,从而达到服务降级的目的</li>
+</ol>
+<h2>开发一个本地存根 Stub</h2>
+<p>本地存根 <em>Stub</em> 由用户来提供,并在服务消费方部署。完整的示例可以在这里 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> 获得。</p>
+<pre><code class="language-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DemoServiceStub</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">DemoService</span> </span>{ <span class="hljs-comment">// #1</span>
+    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> Logger logger = LoggerFactory.getLogger(DemoServiceStub.class);
+
+    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> DemoService demoService;
+
+    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">DemoServiceStub</span><span class="hljs-params">(DemoService demoService)</span> </span>{ <span class="hljs-comment">// #2</span>
+        <span class="hljs-keyword">this</span>.demoService = demoService;
+    }
+
+    <span class="hljs-meta">@Override</span>
+    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">sayHello</span><span class="hljs-params">(String name)</span> </span>{ <span class="hljs-comment">// #3</span>
+        logger.info(<span class="hljs-string">"before execute remote service, parameter: "</span> + name); <span class="hljs-comment">// #4</span>
+        <span class="hljs-keyword">try</span> {
+            String result = demoService.sayHello(name); <span class="hljs-comment">// #5</span>
+            logger.info(<span class="hljs-string">"after execute remote service, result: "</span> + result); <span class="hljs-comment">// #6</span>
+            <span class="hljs-keyword">return</span> result;
+        } <span class="hljs-keyword">catch</span> (Exception e) {
+            logger.warn(<span class="hljs-string">"fail to execute service"</span>, e); <span class="hljs-comment">// #7</span>
+            <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
+        }
+    }
+}
+</code></pre>
+<p>要和框架在一起工作,本地存根的实现需要遵循一些与框架事先做出的约定:</p>
+<ol>
+<li>首先本地存根 <em>Stub</em> 是服务接口的一个实现</li>
+<li>本地存根的实现需要提供一个拷贝构造方法,方便框架将远程调用的 <em>Proxy</em> 对象注入进来</li>
+<li>同样的,本地存根需要提供服务接口中所有方法的实现。在本例中,需要实现 <em>sayHello</em> 方法</li>
+<li>在真正发起远程调用之前,用户可以在本地执行一些操作。在本例中,在日志中记录传入的参数</li>
+<li>通过框架传入的 <em>Proxy</em> 对象真正发起远程调用</li>
+<li>在远程调用结束后,也可以加入本地代码的执行。在本例中,在日志中记录远程调用的返回结果</li>
+<li>如果发生错误的时候,也可以做一些错误恢复的动作。在本例中,在日志中记录异常。当然,如果提供了本地伪装的话,<em>catch</em> 中的逻辑是可以省略掉的</li>
+</ol>
+<p>其中步骤 4、步骤 6、和步骤 7 共同构建了等同于面向切面编程中的概念,分别对应于 <strong>before</strong>、<strong>after-returning</strong>、以及 <strong>after-throwing</strong>。</p>
+<p><em>DemoServiceStub</em> 运行在客户端,要使用本地存根的话,还需要在 <em>stub-consumer.xml</em> 中配置属性 <em>stub</em>。可以简单的通过指定 <em>stub=&quot;true&quot;</em> 来告诉 Dubbo 框架使用本地存根,这个时候,本地存根的包名需要和服务接口的包名一致,类名必须在服务接口的类名后加上 <strong>Stub</strong> 的后缀。例如,当服务接口名是 <em>org.apache.dubbo.samples.stub.api.DemoService</em> 时,本地存根的全类名应该是 <em>org.apache.dubbo.samples.stub.api.DemoServiceStub</em>。</p>
+<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"demoService"</span> <span class="hljs-attr">check</span>=<span class="hljs-string">"false"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"org.apache.dubbo.samples.stub.api.DemoService"</span> <span class="hljs-attr">stub</span>=<span class="hljs-string">"true"</span>/&gt;</span>
+</code></pre>
+<p>如果不希望使用默认的命名规则,也可以直接通过 <em>stub</em> 属性来指定本地存根的全类名。</p>
+<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"demoService"</span> <span class="hljs-attr">check</span>=<span class="hljs-string">"false"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"org.apache.dubbo.samples.stub.api.DemoService"</span> <span class="hljs-attr">stub</span>=<span class="hljs-string">"org.apache.dubbo.samples.stub.impl.DemoStu [...]
+</code></pre>
+<p>启动服务端 <em>StubProvider</em> 后,再运行客户端 <em>StubConsumer</em>,可以通过观察客户端的日志来验证本地存根的运行结果。</p>
+<pre><code class="language-bash">[09/04/19 11:52:21:021 CST] main  INFO api.DemoServiceStub: before execute remote service, parameter: dubbo
+[09/04/19 11:52:21:021 CST] main  INFO api.DemoServiceStub: after execute remote service, result: greeting dubbo
+[09/04/19 11:52:21:021 CST] main  INFO stub.StubConsumer: result: greeting dubbo
+</code></pre>
+<h2>开发一个本地伪装 Mock</h2>
+<p>上面说了本地伪装通常用于在远程调用出错的情况下服务降级。完整的示例可以在这里 <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup> 获得。</p>
+<p>这里通过在服务提供方的代码中睡眠来模拟调用端超时,从而执行本地伪装来做容错处理。</p>
+<pre><code class="language-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DemoServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">DemoService</span> </span>{
+    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">sayHello</span><span class="hljs-params">(String name)</span> </span>{
+        <span class="hljs-keyword">try</span> {
+            Thread.sleep(<span class="hljs-number">5000</span>); <span class="hljs-comment">// #1</span>
+        } <span class="hljs-keyword">catch</span> (InterruptedException e) {
+            e.printStackTrace();
+        }
+        <span class="hljs-keyword">return</span> <span class="hljs-string">"hello "</span> + name; <span class="hljs-comment">// #2</span>
+    }
+}
+</code></pre>
+<ol>
+<li>Dubbo 默认的超时时间是 <em>1000 ms</em>,这里通过睡眠 <em>5000ms</em> 来达到触发超时异常的发生</li>
+<li>由于超时的发生,这个结果并不会被返回给客户端,取而代之的是 <em>org.apache.dubbo.remoting.TimeoutException</em></li>
+</ol>
+<p>在客户端提供本地伪装的实现。当远程调用发生错误的时候,返回给调用方的不是服务端的 &quot;hello name&quot;,取而代之的是 &quot;mock name&quot;。</p>
+<pre><code class="language-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DemoServiceMock</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">DemoService</span> </span>{
+    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> Logger logger = LoggerFactory.getLogger(DemoServiceMock.class);
+
+    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">sayHello</span><span class="hljs-params">(String name)</span> </span>{
+        logger.warn(<span class="hljs-string">"about to execute mock: "</span> + DemoServiceMock.class.getSimpleName());
+        <span class="hljs-keyword">return</span> <span class="hljs-string">"mock "</span> + name;
+    }
+}
+</code></pre>
+<p>同样的,要使用本地伪装的话,还需要在 <em>mock-consumer.xml</em> 中配置属性 <em>mock</em>。可以简单的通过指定 <em>mock=&quot;true&quot;</em> 来告诉 Dubbo 框架使用本地伪装,这个时候,本地伪装的包名需要和服务接口的包名一致,类名必须在服务接口的类名后加上 <strong>Mock</strong> 的后缀。例如,当服务接口名是 <em>org.apache.dubbo.samples.stub.api.DemoService</em> 时,本地存根的全类名应该是 <em>org.apache.dubbo.samples.stub.api.DemoServiceMock</em>。</p>
+<pre><code class="language-xml">    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"demoService"</span> <span class="hljs-attr">check</span>=<span class="hljs-string">"false"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"org.apache.dubbo.samples.mock.api.DemoService"</span>
+                     <span class="hljs-attr">mock</span>=<span class="hljs-string">"true"</span>/&gt;</span>
+</code></pre>
+<p>如果不希望使用默认的命名规则,也可以直接通过 <em>mock</em> 属性来指定本地伪装的全类名。</p>
+<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"demoService"</span> <span class="hljs-attr">check</span>=<span class="hljs-string">"false"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"org.apache.dubbo.samples.mock.api.DemoService"</span> <span class="hljs-attr">stub</span>=<span class="hljs-string">"org.apache.dubbo.samples.mock.impl.DemoMoc [...]
+</code></pre>
+<p>通过提供一个本地伪装的类,可以最大限度的控制出错之后的容错逻辑。有的时候,业务上并不需要这样灵活的机制,只有返回一个默认值的诉求,这个时候提供一个完整的本地伪装的实现就显得有点重了。或者线上出错的时候,应用并没有打包本地伪装,需要通过推送规则的方式临时对服务降级。Dubbo 框架为上面的这两种诉求都提供了快捷方式,帮助用户快速配置服务降级。</p>
+<p>启动服务端 <em>MockProvider</em> 之后,然后再执行 <em>MockConsumer</em> 就可以看到下面的结果:</p>
+<pre><code class="language-bash">Caused by: org.apache.dubbo.remoting.TimeoutException: Waiting server-side response timeout by scan timer. start time: 2019-04-09 14:20:48.061, end time: 2019-04-09 14:20:49.077, client elapsed: 0 ms, server elapsed: 1015 ms, timeout: 1000 ms, request: Request [id=2, version=2.0.2, twoway=<span class="hljs-literal">true</span>, event=<span class="hljs-literal">false</span>, broken=<span class="hljs-literal">false</span>, data=RpcInvocation [methodName=say [...]
+	at org.apache.dubbo.remoting.exchange.support.DefaultFuture.returnFromResponse(DefaultFuture.java:295)
+	at org.apache.dubbo.remoting.exchange.support.DefaultFuture.get(DefaultFuture.java:191)
+	at org.apache.dubbo.remoting.exchange.support.DefaultFuture.get(DefaultFuture.java:164)
+	at org.apache.dubbo.rpc.protocol.dubbo.DubboInvoker.doInvoke(DubboInvoker.java:108)
+	at org.apache.dubbo.rpc.protocol.AbstractInvoker.invoke(AbstractInvoker.java:157)
+	at org.apache.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:88)
+	at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper<span class="hljs-variable">$1</span>.invoke(ProtocolFilterWrapper.java:73)
+	at org.apache.dubbo.rpc.protocol.dubbo.filter.FutureFilter.invoke(FutureFilter.java:49)
+	at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper<span class="hljs-variable">$1</span>.invoke(ProtocolFilterWrapper.java:73)
+	at org.apache.dubbo.rpc.filter.ConsumerContextFilter.invoke(ConsumerContextFilter.java:54)
+	at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper<span class="hljs-variable">$1</span>.invoke(ProtocolFilterWrapper.java:73)
+	at org.apache.dubbo.rpc.listener.ListenerInvokerWrapper.invoke(ListenerInvokerWrapper.java:78)
+	at org.apache.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:56)
+	at org.apache.dubbo.rpc.cluster.support.FailoverClusterInvoker.doInvoke(FailoverClusterInvoker.java:80)
+	... 5 more
+[09/04/19 02:20:49:049 CST] main  WARN api.DemoServiceMock: about to execute mock: DemoServiceMock
+[09/04/19 02:20:49:049 CST] main  INFO mock.MockConsumer: result: mock world
+</code></pre>
+<p>下面通过规则推送为例展示这种快捷方式的用法,更多的用法请参考 Dubbo 官方用户手册 <sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>。通过向配置中心推送指定服务的配置,就可以做到动态服务降级的目的。</p>
+<pre><code class="language-yaml"><span class="hljs-bullet">-</span><span class="hljs-bullet">--</span> <span class="hljs-comment"># 1</span>
+<span class="hljs-attr">configVersion:</span> <span class="hljs-string">v2.7</span>
+<span class="hljs-attr">scope:</span> <span class="hljs-string">service</span>
+<span class="hljs-attr">  key:</span> <span class="hljs-string">org.apache.dubbo.samples.mock.api.DemoService</span> <span class="hljs-comment">#2</span>
+<span class="hljs-attr">  enabled:</span> <span class="hljs-literal">true</span>
+<span class="hljs-attr">  configs:</span>
+<span class="hljs-attr">    - addresses:</span> <span class="hljs-string">[0.0.0.0]</span>
+<span class="hljs-attr">      side:</span> <span class="hljs-string">consumer</span> <span class="hljs-comment">#3</span>
+<span class="hljs-attr">      parameters:</span>
+<span class="hljs-attr">        mock:</span> <span class="hljs-string">return</span> <span class="hljs-string">configured-mock-value</span> <span class="hljs-comment">#4</span>
+      <span class="hljs-string">...</span>
+</code></pre>
+<ol>
+<li>以 <em>Zookeeper</em> 为例,规则的路径是 /dubbo/config/org.apache.dubbo.samples.mock.api.DemoService/configurators</li>
+<li>该规则作用在 <em>org.apache.dubbo.samples.mock.api.DemoService</em> 服务上</li>
+<li>该规则作用在客户端</li>
+<li>当错误发送时,对服务的调用返回默认值 <em>configured-mock-value</em></li>
+</ol>
+<p>启动服务端 <em>MockProvider</em> 之后,再执行例子<sup class="footnote-ref"><a href="#fn2" id="fnref2:1">[2:1]</a></sup>中的 <em>Configurator</em> 完成对服务降级规则的配置,然后再执行 <em>MockConsumer</em> 就可以看到下面的结果:</p>
+<pre><code class="language-bash">[09/04/19 02:19:01:001 CST] main  INFO integration.AbstractConfiguratorListener:  [DUBBO] Notification of overriding rule, change <span class="hljs-built_in">type</span> is: MODIFIED, raw config content is:
+ ---
+configVersion: v2.7
+scope: service
+key: org.apache.dubbo.samples.mock.api.DemoService
+enabled: <span class="hljs-literal">true</span>
+configs:
+- addresses: [0.0.0.0]
+  side: consumer
+  parameters:
+    mock: <span class="hljs-built_in">return</span> configured-mock-value
+...
+, dubbo version: 2.7.1, current host: 30.5.125.99
+
+...
+
+Caused by: org.apache.dubbo.remoting.TimeoutException: Waiting server-side response timeout. start time: 2019-04-09 14:19:03.737, end time: 2019-04-09 14:19:04.741, client elapsed: 1 ms, server elapsed: 1002 ms, timeout: 1000 ms, request: Request [id=2, version=2.0.2, twoway=<span class="hljs-literal">true</span>, event=<span class="hljs-literal">false</span>, broken=<span class="hljs-literal">false</span>, data=RpcInvocation [methodName=sayHello, parameterTypes=[class java.lang.String], [...]
+	at org.apache.dubbo.remoting.exchange.support.DefaultFuture.get(DefaultFuture.java:188)
+	at org.apache.dubbo.remoting.exchange.support.DefaultFuture.get(DefaultFuture.java:164)
+	at org.apache.dubbo.rpc.protocol.dubbo.DubboInvoker.doInvoke(DubboInvoker.java:108)
+	at org.apache.dubbo.rpc.protocol.AbstractInvoker.invoke(AbstractInvoker.java:157)
+	at org.apache.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:88)
+	at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper<span class="hljs-variable">$1</span>.invoke(ProtocolFilterWrapper.java:73)
+	at org.apache.dubbo.rpc.protocol.dubbo.filter.FutureFilter.invoke(FutureFilter.java:49)
+	at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper<span class="hljs-variable">$1</span>.invoke(ProtocolFilterWrapper.java:73)
+	at org.apache.dubbo.rpc.filter.ConsumerContextFilter.invoke(ConsumerContextFilter.java:54)
+	at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper<span class="hljs-variable">$1</span>.invoke(ProtocolFilterWrapper.java:73)
+	at org.apache.dubbo.rpc.listener.ListenerInvokerWrapper.invoke(ListenerInvokerWrapper.java:78)
+	at org.apache.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:56)
+	at org.apache.dubbo.rpc.cluster.support.FailoverClusterInvoker.doInvoke(FailoverClusterInvoker.java:80)
+	... 5 more
+[09/04/19 02:19:04:004 CST] main  INFO mock.MockConsumer: result: configured-mock-value
+</code></pre>
+<h2>总结</h2>
+<p>本文介绍了 Dubbo 中本地存根和本地伪装的概念和用法。从本质来讲,本地存根和本地伪装等同于面向切面编程中的概念,通过诸如 Spring 框架提供的 <em>AOP</em> 编程可以达到同样的目的。通过本文如何开发一个本地存根和本地伪装的示例,读者可以直观的感受到通过框架提供的机制更加方便快捷。同时,对于很多简单的场景和动态配置推送的场景,框架提供了仅通过配置而无需编码的方式来满足,进一步提升了框架使用者的效率。</p>
+<hr class="footnotes-sep">
+<section class="footnotes">
+<ol class="footnotes-list">
+<li id="fn1" class="footnote-item"><p><a href="https://github.com/apache/incubator-dubbo-samples/tree/master/dubbo-samples-stub">https://github.com/apache/incubator-dubbo-samples/tree/master/dubbo-samples-stub</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p>
+</li>
+<li id="fn2" class="footnote-item"><p><a href="https://github.com/apache/incubator-dubbo-samples/tree/master/dubbo-samples-mock">https://github.com/apache/incubator-dubbo-samples/tree/master/dubbo-samples-mock</a> <a href="#fnref2" class="footnote-backref">↩︎</a> <a href="#fnref2:1" class="footnote-backref">↩︎</a></p>
+</li>
+<li id="fn3" class="footnote-item"><p><a href="http://dubbo.apache.org/zh-cn/docs/user/demos/local-mock.html">http://dubbo.apache.org/zh-cn/docs/user/demos/local-mock.html</a> <a href="#fnref3" class="footnote-backref">↩︎</a></p>
+</li>
+</ol>
+</section>
+</section><footer class="footer-container"><div class="footer-body"><img src="/img/dubbo_gray.png"/><img class="apache" src="/img/apache_logo.png"/><div class="cols-container"><div class="col col-12"><h3>Disclaimer</h3><p>Apache Dubbo is an effort undergoing incubation at The Apache Software Foundation (ASF), sponsored by the Incubator. Incubation is required of all newly accepted projects until a further review indicates that the infrastructure, communications, and decision making proce [...]
+	<script src="https://f.alicdn.com/react/15.4.1/react-with-addons.min.js"></script>
+	<script src="https://f.alicdn.com/react/15.4.1/react-dom.min.js"></script>
+	<script>
+		window.rootPath = '';
+  </script>
+  <script src="/build/blogDetail.js"></script>
+  <!-- Global site tag (gtag.js) - Google Analytics -->
+	<script async src="https://www.googletagmanager.com/gtag/js?id=UA-112489517-1"></script>
+	<script>
+		window.dataLayer = window.dataLayer || [];
+		function gtag(){dataLayer.push(arguments);}
+		gtag('js', new Date());
+
+		gtag('config', 'UA-112489517-1');
+	</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/zh-cn/blog/dubbo-stub-mock.json b/zh-cn/blog/dubbo-stub-mock.json
new file mode 100644
index 0000000..f9d1899
--- /dev/null
+++ b/zh-cn/blog/dubbo-stub-mock.json
@@ -0,0 +1,6 @@
+{
+  "filename": "dubbo-stub-mock.md",
+  "__html": "<h1>本地存根和本地伪装</h1>\n<h2>基本概念</h2>\n<p>典型的 RPC 调用客户端是依赖并且只依赖接口编程来进行远程调用的。在真正发起远程调用之前,用户往往需要做一些预处理的工作,比如提前校验参数。在拿到返回调用结果之后,用户可能需要缓存结果,或者是在调用失败的时候构造容错数据,而不是简单的抛出异常。</p>\n<p>这个时候,用户可以编写出类似以下的代码来处理上面提出的这些场景:</p>\n<pre><code class=\"language-java\"><span class=\"hljs-keyword\">try</span> {\n    preProcess();\n    <span class=\"hljs-keyword\">return</span> service.invoke(...);\n} <span class=\"hljs-keyword\">catch</span> (Throwable e) {\n    <span class=\"hljs-keyword\">return</spa [...]
+  "link": "/zh-cn/blog/dubbo-stub-mock.html",
+  "meta": {}
+}
\ No newline at end of file
diff --git a/zh-cn/blog/index.html b/zh-cn/blog/index.html
index 39ccbc4..84e8e6d 100644
--- a/zh-cn/blog/index.html
+++ b/zh-cn/blog/index.html
@@ -12,7 +12,7 @@
 	<link rel="stylesheet" href="/build/blog.css" />
 </head>
 <body>
-	<div id="root"><div class="blog-list-page" data-reactroot=""><header class="header-container header-container-normal"><div class="header-body"><a href="/zh-cn/index.html"><img class="logo" src="/img/dubbo_colorful.png"/></a><div class="search search-normal"><span class="icon-search"></span></div><span class="language-switch language-switch-normal">En</span><div class="header-menu"><img class="header-menu-toggle" src="/img/menu_gray.png"/><ul><li class="menu-item menu-item-normal"><a hre [...]
+	<div id="root"><div class="blog-list-page" data-reactroot=""><header class="header-container header-container-normal"><div class="header-body"><a href="/zh-cn/index.html"><img class="logo" src="/img/dubbo_colorful.png"/></a><div class="search search-normal"><span class="icon-search"></span></div><span class="language-switch language-switch-normal">En</span><div class="header-menu"><img class="header-menu-toggle" src="/img/menu_gray.png"/><ul><li class="menu-item menu-item-normal"><a hre [...]
 	<script src="https://f.alicdn.com/react/15.4.1/react-with-addons.min.js"></script>
 	<script src="https://f.alicdn.com/react/15.4.1/react-dom.min.js"></script>
 	<script>