This is an automated email from the ASF dual-hosted git repository.
albumenj pushed a commit to branch refactor/next-2
in repository https://gitbox.apache.org/repos/asf/dubbo-website.git
The following commit(s) were added to refs/heads/refactor/next-2 by this push:
new fe953dbdd3 Update apisix & shenyu & hystrix & sentinel & seata
fe953dbdd3 is described below
commit fe953dbdd344f870d06a78b3c62bca684a5baf3e
Author: Albumen Kevin <[email protected]>
AuthorDate: Fri Feb 10 14:17:01 2023 +0800
Update apisix & shenyu & hystrix & sentinel & seata
---
.../overview/what/ecosystem/gateway/apisix.md | 217 ++++++++++-
.../overview/what/ecosystem/gateway/shenyu.md | 401 ++++++++++++++++++++-
.../overview/what/ecosystem/rate-limit/hystrix.md | 168 ++++++++-
.../overview/what/ecosystem/rate-limit/sentinel.md | 131 ++++++-
.../overview/what/ecosystem/transaction/seata.md | 208 ++++++++++-
5 files changed, 1120 insertions(+), 5 deletions(-)
diff --git a/content/zh-cn/overview/what/ecosystem/gateway/apisix.md
b/content/zh-cn/overview/what/ecosystem/gateway/apisix.md
index 28a964b6e7..972cc29e33 100644
--- a/content/zh-cn/overview/what/ecosystem/gateway/apisix.md
+++ b/content/zh-cn/overview/what/ecosystem/gateway/apisix.md
@@ -4,4 +4,219 @@ title: "Apache APISIX"
linkTitle: "Apache APISIX"
weight: 10
description: ""
----
\ No newline at end of file
+---
+
+## 背景
+
+[Apache Dubbo](/zh-cn/) 是由阿里巴巴开源并捐赠给 Apache 的微服务开发框架,它提供了 RPC
通信与微服务治理两大关键能力。不仅经过了阿里电商场景中海量流量的验证,也在国内的技术公司中被广泛落地。
+
+在实际应用场景中,Apache Dubbo 一般会作为后端系统间 RPC 调用的实现框架,当需要提供 HTTP 接口给到前端时,会通过一个「胶水层」将
Dubbo Service 包装成 HTTP 接口,再交付到前端系统。
+
+[Apache APISIX](https://apisix.apache.org/) 是 Apache
软件基金会的顶级开源项目,也是当前最活跃的开源网关项目。作为一个动态、实时、高性能的开源 API 网关,Apache APISIX
提供了负载均衡、动态上游、灰度发布、服务熔断、身份认证、可观测性等丰富的流量管理功能。
+
+得益于 Apache Dubbo 的应用场景优势,Apache APISIX 基于开源项目 tengine/mod_dubbo 模块为 Apache
Dubbo 服务配备了HTTP 网关能力。通过 dubbo-proxy 插件,可以轻松地将 Dubbo Service 发布为 HTTP 服务。
+
+
+
+## 如何使用
+
+### 入门篇:安装使用
+
+>这里我们建议使用 Apache APISIX 2.11 版本镜像进行安装。该版本的 APISIX-Base 中已默认编译了 Dubbo 模块,可直接使用
`dubbo-proxy` 插件。
+在接下来的操作中,我们将使用 [`dubbo-samples`](https://github.com/apache/dubbo-samples)
项目进行部分展示。该项目是一些使用 Apache Dubbo 实现的 Demo 应用,本文中我们采用其中的一个子模块作为 Dubbo Provider。
+
+在进入正式操作前,我们先简单看下 Dubbo 接口的定义、配置以及相关实现。
+
+#### 接口实现一览
+
+```java
+public interface DemoService {
+ /**
+ * standard samples dubbo infterace demo
+ * @param context pass http infos
+ * @return Map<String, Object></> pass to response http
+ **/
+ Map<String, Object> apisixDubbo(Map<String, Object> httpRequestContext);
+}
+```
+
+如上所示,Dubbo 接口的定义是固定的。即方法参数中 `Map` 表示 APISIX 传递给 Dubbo Provider 关于 HTTP request
的一些信息(如:header、body...)。而方法返回值的 `Map` 表示 Dubbo Provider 传递给 APISIX 要如何返回 HTTP
response 的一些信息。
+
+接口信息配置好之后可通过 XML 配置方式发布 DemoService。
+
+```xml
+<!-- service implementation, as same as regular local bean -->
+<bean id="demoService"
class="org.apache.dubbo.samples.provider.DemoServiceImpl"/>
+
+<!-- declare the service interface to be exported -->
+<dubbo:service interface="org.apache.dubbo.samples.apisix.DemoService"
ref="demoService"/>
+```
+
+通过上述配置后,Consumer 可通过 `org.apache.dubbo.samples.apisix.DemoService`
访问其中的`apisixDubbo` 方法。具体接口实现如下:
+
+```java
+public class DemoServiceImpl implements DemoService {
+ @Override
+ public Map<String, Object> apisixDubbo(Map<String, Object>
httpRequestContext) {
+ for (Map.Entry<String, Object> entry : httpRequestContext.entrySet()) {
+ System.out.println("Key = " + entry.getKey() + ", Value = " +
entry.getValue());
+ }
+ Map<String, Object> ret = new HashMap<String, Object>();
+ ret.put("body", "dubbo success\n"); // http response body
+ ret.put("status", "200"); // http response status
+ ret.put("test", "123"); // http response header
+ return ret;
+ }
+}
+```
+
+上述代码中,`DemoServiceImpl` 会打印接收到的 `httpRequestContext`,并通过返回包含有指定 Key 的 Map
对象去描述该 Dubbo 请求的 HTTP 响应。
+
+#### 操作步骤
+
+1. 启动
[`dubbo-samples`](https://github.com/apache/dubbo-samples/tree/master/2-advanced/dubbo-samples-tengine#install-dubbo)。
+2. 在 `config.yaml` 文件中进行 `dubbo-proxy` 插件启用。
+
+```yaml
+# Add this in config.yaml
+plugins:
+ - ... # plugin you need
+ - dubbo-proxy
+```
+
+3. 创建指向 Dubbo Provider 的 Upstream。
+
+```shell
+curl http://127.0.0.1:9180/apisix/admin/upstreams/1 -H 'X-API-KEY:
edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+{
+ "nodes": {
+ "127.0.0.1:20880": 1
+ },
+ "type": "roundrobin"
+}'
+```
+
+4. 为 DemoService 暴露一个 HTTP 路由。
+
+```shell
+curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY:
edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+{
+ "host": "example.org"
+ "uris": [
+ "/demo"
+ ],
+ "plugins": {
+ "dubbo-proxy": {
+ "service_name": "org.apache.dubbo.samples.apisix.DemoService",
+ "service_version": "0.0.0",
+ "method": "apisixDubbo"
+ }
+ },
+ "upstream_id": 1
+}'
+```
+
+5. 使用 curl 命令请求 Apache APISIX,并查看返回结果。
+
+```shell
+curl http://127.0.0.1:9080/demo -H "Host: example.org" -X POST --data
'{"name": "hello"}'
+< HTTP/1.1 200 OK
+< Date: Sun, 26 Dec 2021 11:33:27 GMT
+< Content-Type: text/plain; charset=utf-8
+< Content-Length: 14
+< Connection: keep-alive
+< test: 123
+< Server: APISIX/2.11.0
+<
+dubbo success
+```
+
+:::note 说明
+上述代码返回中包含了 `test: 123` Header,以及 `dubbo success` 字符串作为 Body 体。这与我们在
`DemoServiceImpl` 编码的预期效果一致。
+:::
+
+6. 查看 Dubbo Provider 的日志。
+
+```
+Key = content-length, Value = 17
+Key = host, Value = example.org
+Key = content-type, Value = application/x-www-form-urlencoded
+Key = body, Value = [B@70754265
+Key = accept, Value = */*
+Key = user-agent, Value = curl/7.80.0
+```
+
+:::note 说明
+通过 `httpRequestContext` 可以拿到 HTTP 请求的 Header 和 Body。其中 Header 会作为 Map 元素,而
Body 中 Key 值是固定的字符串"body",Value 则代表 Byte 数组。
+:::
+
+### 进阶篇:复杂场景示例
+
+在上述的简单用例中可以看出,我们确实通过 Apache APISIX 将 Dubbo Service 发布为一个 HTTP
服务,但是在使用过程中的限制也非常明显。比如:接口的参数和返回值都必须要是 `Map<String, Object>`。
+
+那么,如果项目中出现已经定义好、但又不符合上述限制的接口,该如何通过 Apache APISIX 来暴露 HTTP 服务呢?
+
+#### 操作步骤
+
+针对上述场景,我们可以通过 HTTP Request Body 描述要调用的 Service 和 Method 以及对应参数,再利用 Java
的反射机制实现目标方法的调用。最后将返回值序列化为 JSON,并写入到 HTTP Response Body 中。
+
+这样就可以将 Apache APISIX 的 「HTTP to Dubbo」 能力进一步加强,并应用到所有已存在的 Dubbo Service
中。具体操作可参考下方:
+
+1. 为已有项目增加一个 Dubbo Service 用来统一处理 HTTP to Dubbo 的转化。
+
+```java
+public class DubboInvocationParameter {
+ private String type;
+ private String value;
+}
+public class DubboInvocation {
+ private String service;
+ private String method;
+ private DubboInvocationParameter[] parameters;
+}
+public interface HTTP2DubboService {
+ Map<String, Object> invoke(Map<String, Object> context) throws Exception;
+}
+@Component
+public class HTTP2DubboServiceImpl implements HTTP2DubboService {
+ @Autowired
+ private ApplicationContext appContext;
+ @Override
+ public Map<String, Object> invoke(Map<String, Object> context) throws
Exception {
+ DubboInvocation invocation = JSONObject.parseObject((byte[])
context.get("body"), DubboInvocation.class);
+ Object[] args = new Object[invocation.getParameters().size()];
+ for (int i = 0; i < args.length; i++) {
+ DubboInvocationParameter parameter =
invocation.getParameters().get(i);
+ args[i] = JSONObject.parseObject(parameter.getValue(),
Class.forName(parameter.getType()));
+ }
+ Object svc =
appContext.getBean(Class.forName(invocation.getService()));
+ Object result =
svc.getClass().getMethod(invocation.getMethod()).invoke(args);
+ Map<String, Object> httpResponse = new HashMap<>();
+ httpResponse.put("status", 200);
+ httpResponse.put("body", JSONObject.toJSONString(result));
+ return httpResponse;
+ }
+}
+```
+
+2. 通过如下命令请求来发起相关调用。
+
+```shell
+curl http://127.0.0.1:9080/demo -H "Host: example.org" -X POST --data '
+{
+ "service": "org.apache.dubbo.samples.apisix.DemoService",
+ "method": "createUser",
+ "parameters": [
+ {
+ "type": "org.apache.dubbo.samples.apisix.User",
+ "value": "{'name': 'hello'}"
+ }
+ ]
+}'
+```
+
+## 总结
+
+本文为大家介绍了如何借助 Apache APISIX 实现 Dubbo Service 的代理,通过引入 `dubbo-proxy` 插件便可为 Dubbo
框架的后端系统构建更简单更高效的流量链路。
+
+希望通过上述操作步骤和用例场景分享,能为大家在相关场景的使用提供借鉴思路。更多关于 `dubbo-proxy`
插件的介绍与使用可参考[官方文档](https://apisix.apache.org/docs/apisix/plugins/dubbo-proxy/)。
diff --git a/content/zh-cn/overview/what/ecosystem/gateway/shenyu.md
b/content/zh-cn/overview/what/ecosystem/gateway/shenyu.md
index d5feeeb20e..bfd842c782 100644
--- a/content/zh-cn/overview/what/ecosystem/gateway/shenyu.md
+++ b/content/zh-cn/overview/what/ecosystem/gateway/shenyu.md
@@ -4,4 +4,403 @@ title: "Apache Shenyu"
linkTitle: "Apache Shenyu"
weight: 20
description: ""
----
\ No newline at end of file
+---
+
+
+
+
+## 1. 介绍
+
+- Apache ShenYu
+
+
+
+
+[Apache ShenYu(Incubating)](https://shenyu.apache.org/zh/docs/index)
是一个异步的,高性能的,跨语言的,响应式的 `API`
网关。兼容各种主流框架体系,支持热插拔,用户可以定制化开发,满足用户各种场景的现状和未来需求,经历过大规模场景的锤炼。
+
+2021年5月,`ShenYu`捐献给 `Apache` 软件基金会,Apache 基金会全票通过,顺利进入孵化器。
+
+
+- Apache Dubbo
+
+`Apache Dubbo` 是一款微服务开发框架,它提供了 `RPC` 通信 与 微服务治理 两大关键能力。这意味着,使用 `Dubbo`
开发的微服务,将具备相互之间的远程发现与通信能力, 同时利用 Dubbo 提供的丰富服务治理能力,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。同时
`Dubbo` 是高度可扩展的,用户几乎可以在任意功能点去定制自己的实现,以改变框架的默认行为来满足自己的业务需求。
+
+
+## 2. Dubbo快速开始
+
+本小节介绍如何将`Dubbo`服务接入到`ShenYu`网关,您可以直接在工程下找到本小节的[示例代码](https://github.com/apache/shenyu/tree/master/shenyu-examples/shenyu-examples-dubbo)
。
+
+
+### 2.1 启动shenyu-admin
+
+`shenyu-admin`是`Apache ShenYu`后台管理系统, 启动的方式有多种,本文通过
`[本地部署](https://shenyu.apache.org/zh/docs/deployment/deployment-local)`
的方式启动。启动成功后,需要在基础配置`->`插件管理中,把`dubbo` 插件设置为开启,并设置你的注册地址,请确保注册中心已经开启。
+
+
+
+
+### 2.2 启动shenyu网关
+
+在这里通过
[源码](https://github.com/apache/incubator-shenyu/tree/master/shenyu-bootstrap)
的方式启动,直接运行`shenyu-bootstrap`中的`ShenyuBootstrapApplication`。
+
+在启动前,请确保网关已经引入相关依赖。如果客户端是`apache dubbo`,注册中心使用`zookeeper`,请参考如下配置:
+
+```java
+ <!-- apache shenyu apache dubbo plugin start-->
+ <dependency>
+ <groupId>org.apache.shenyu</groupId>
+
<artifactId>shenyu-spring-boot-starter-plugin-apache-dubbo</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo</artifactId>
+ <version>2.7.5</version>
+ </dependency>
+ <!-- Dubbo zookeeper registry dependency start -->
+ <dependency>
+ <groupId>org.apache.curator</groupId>
+ <artifactId>curator-client</artifactId>
+ <version>4.0.1</version>
+ <exclusions>
+ <exclusion>
+ <artifactId>log4j</artifactId>
+ <groupId>log4j</groupId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.curator</groupId>
+ <artifactId>curator-framework</artifactId>
+ <version>4.0.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.curator</groupId>
+ <artifactId>curator-recipes</artifactId>
+ <version>4.0.1</version>
+ </dependency>
+ <!-- Dubbo zookeeper registry dependency end -->
+ <!-- apache dubbo plugin end-->
+```
+
+### 2.3 启动shenyu-examples-dubbo
+
+以官网提供的例子为例
[shenyu-examples-dubbo](https://github.com/apache/shenyu/tree/master/shenyu-examples/shenyu-examples-dubbo)
。 假如`dubbo`服务定义如下:
+
+```xml
+<beans /* ...... * />
+
+ <dubbo:application name="test-dubbo-service"/>
+ <dubbo:registry address="${dubbo.registry.address}"/>
+ <dubbo:protocol name="dubbo" port="20888"/>
+
+ <dubbo:service timeout="10000"
interface="org.apache.shenyu.examples.dubbo.api.service.DubboTestService"
ref="dubboTestService"/>
+
+</beans>
+```
+
+声明应用服务名称,注册中心地址,使用`dubbo`协议,声明服务接口,对应接口实现类:
+
+```java
+/**
+ * DubboTestServiceImpl.
+ */
+@Service("dubboTestService")
+public class DubboTestServiceImpl implements DubboTestService {
+
+ @Override
+ @ShenyuDubboClient(path = "/findById", desc = "Query by Id")
+ public DubboTest findById(final String id) {
+ return new DubboTest(id, "hello world shenyu Apache, findById");
+ }
+ //......
+}
+```
+
+在接口实现类中,使用注解`@ShenyuDubboClient`向`shenyu-admin`注册服务。
+
+在配置文件`application.yml`中的配置信息:
+
+```yaml
+server:
+ port: 8011
+ address: 0.0.0.0
+ servlet:
+ context-path: /
+spring:
+ main:
+ allow-bean-definition-overriding: true
+dubbo:
+ registry:
+ address: zookeeper://localhost:2181 # dubbo使用的注册中心
+
+shenyu:
+ register:
+ registerType: http #注册方式
+ serverLists: http://localhost:9095 #注册地址
+ props:
+ username: admin
+ password: 123456
+ client:
+ dubbo:
+ props:
+ contextPath: /dubbo
+ appName: dubbo
+```
+
+在配置文件中,声明`dubbo`使用的注册中心地址,`dubbo`服务向`shenyu-admin`注册,使用的方式是`http`,注册地址是`http://localhost:9095`。
+
+关于注册方式的使用,请参考
`[应用客户端接入](https://shenyu.apache.org/docs/design/register-center-design/)` 。
+
+
+### 2.4 调用dubbo服务
+
+`shenyu-examples-dubbo`项目成功启动之后会自动把加 `@ShenyuDubboClient` 注解的接口方法注册到网关。
+
+
+打开 `插件列表 -> Proxy -> dubbo` 可以看到插件规则配置列表:
+
+
+
+
+注册成功的选择器信息:
+
+
+
+注册成功的规则信息:
+
+
+
+> 选择器和规则是 `Apache ShenYu`
网关中最灵魂的东西。掌握好它,你可以对任何流量进行管理。对应为选择器与规则里面的匹配条件(conditions),根据不同的流量筛选规则,我们可以处理各种复杂的场景。流量筛选可以从`Header`,
`URI`, `Query`, `Cookie` 等等Http请求获取数据。
+>
+> 然后可以采用
`Match`,`=`,`Regex`,`Groovy`,`Exclude`等匹配方式,匹配出你所预想的数据。多组匹配添加可以使用`And/Or`的匹配策略。
+>
+> 具体的介绍与使用请看:
`[选择器与规则管理](https://shenyu.apache.org/zh/docs/user-guide/admin-usage/selector-and-rule)`
。
+
+发起`GET`请求,通过`ShenYu`网关调用`dubbo`服务:
+
+```
+GET http://localhost:9195/dubbo/findById?id=100
+Accept: application/json
+```
+
+成功响应之后,结果如下:
+
+```
+{
+ "name": "hello world shenyu Apache, findById",
+ "id": "100"
+}
+```
+
+至此,就成功的通过`http`请求访问`dubbo`服务了,`ShenYu`网关通过`shenyu-plugin-dubbo`模块将`http`协议转成了`dubbo`协议。
+
+
+## 3. 深入理解Dubbo插件
+
+在运行上述`demo`的过程中,是否存在一些疑问:
+
+- `dubbo`服务是如何注册到`shenyu-admin`?
+- `shenyu-admin`是如何将数据同步到`ShenYu`网关?
+- `DubboPlugin`是如何将`http`协议转换到到dubbo协议?
+
+带着这些疑问,来深入理解`dubbo`插件。
+
+
+### 3.1 应用客户端接入
+
+应用客户端接入是指将微服务接入到`Apache ShenYu`网关,当前支持`Http`、 `Dubbo`、 `Spring Cloud`、 `gRPC`、
`Motan`、 `Sofa`、 `Tars`等协议的接入。
+
+将应用客户端接入到`Apache
ShenYu`网关是通过注册中心来实现的,涉及到客户端注册和服务端同步数据。注册中心支持`Http`、`Zookeeper`、`Etcd`、`Consul`和`Nacos`。默认是通过`Http`方式注册。
+
+客户端接入的相关配置请参考
`[客户端接入配置](https://shenyu.apache.org/zh/docs/user-guide/register-center-access)`
。
+
+#### 3.1.1 客户端注册
+
+
+
+在你的微服务配置中声明注册中心客户端类型,如`Http`或`Zookeeper`。
+应用程序启动时使用`SPI`方式加载并初始化对应注册中心客户端,通过实现`Spring
Bean`相关的后置处理器接口,在其中获取需要进行注册的服务接口信息,将获取的信息放入`Disruptor`中。
+
+注册中心客户端从`Disruptor`中读取数据,并将接口信息注册到`shenyu-admin`,`Disruptor`在其中起数据与操作解耦的作用,利于扩展。
+
+#### 3.1.2 服务端注册
+
+
+
+在`shenyu-admin`配置中声明注册中心服务端类型,如`Http`或`Zookeeper`。当`shenyu-admin`启动时,读取配置类型,加载并初始化对应的注册中心服务端,注册中心服务端收到`shenyu-client`注册的接口信息后,将其放入`Disruptor`中,然后会触发注册处理逻辑,将服务接口信息更新并发布同步事件。
+
+`Disruptor`在其中起到数据与操作解耦,利于扩展。如果注册请求过多,导致注册异常,也有数据缓冲作用。
+
+### 3.2 数据同步原理
+
+数据同步是指在 `shenyu-admin` 后台操作数据以后,使用何种策略将数据同步到 `Apache ShenYu` 网关。`Apache
ShenYu` 网关当前支持`ZooKeeper`、`WebSocket`、`Http长轮询`、`Nacos` 、`Etcd` 和 `Consul`
进行数据同步。默认是通过`WebSocket`进行数据同步。
+
+数据同步的相关配置请参考
`[数据同步配置](https://shenyu.apache.org/zh/docs/user-guide/use-data-sync)` 。
+
+#### 3.2.1 数据同步的意义
+
+网关是流量请求的入口,在微服务架构中承担了非常重要的角色,网关高可用的重要性不言而喻。在使用网关的过程中,为了满足业务诉求,经常需要变更配置,比如流控规则、路由规则等等。因此,网关动态配置是保障网关高可用的重要因素。
+
+当前数据同步特性如下:
+
+- 所有的配置都缓存在 `Apache ShenYu` 网关内存中,每次请求都使用本地缓存,速度非常快。
+- 用户可以在 `shenyu-admin` 后台任意修改数据,并马上同步到网关内存。
+- 支持 `Apache ShenYu` 的插件、选择器、规则数据、元数据、签名数据等数据同步。
+- 所有插件的选择器,规则都是动态配置,立即生效,不需要重启服务。
+- 数据同步方式支持 `Zookeeper`、`Http 长轮询`、`Websocket`、`Nacos`、`Etcd` 和 `Consul`。
+
+#### 3.2.2 数据同步原理分析
+
+下图展示了 `Apache ShenYu` 数据同步的流程,`Apache ShenYu`
网关在启动时,会从配置服务同步配置数据,并且支持推拉模式获取配置变更信息,然后更新本地缓存。管理员可以在管理后台(`shenyu-admin`),变更用户权限、规则、插件、流量配置,通过推拉模式将变更信息同步给
`Apache ShenYu` 网关,具体是 `push` 模式,还是 `pull` 模式取决于使用哪种同步方式。
+
+
+
+在最初的版本中,配置服务依赖 `Zookeeper` 实现,管理后台将变更信息 `push` 给网关。而现在可以支持
`WebSocket`、`Http长轮询`、`Zookeeper`、`Nacos`、`Etcd` 和 `Consul`,通过在配置文件中设置
`shenyu.sync.${strategy}` 指定对应的同步策略,默认使用 `webosocket`
同步策略,可以做到秒级数据同步。但是,有一点需要注意的是,`Apache ShenYu`网关 和 `shenyu-admin` 必须使用相同的同步策略。
+
+如下图所示,`shenyu-admin` 在用户发生配置变更之后,会通过 `EventPublisher` 发出配置变更通知,由
`EventDispatcher`
处理该变更通知,然后根据配置的同步策略(`http、weboscket、zookeeper、naocs、etcd、consul`),将配置发送给对应的事件处理器。
+
+- 如果是 `websocket` 同步策略,则将变更后的数据主动推送给 `shenyu-web`,并且在网关层,会有对应的
`WebsocketDataHandler` 处理器来处理 `shenyu-admin` 的数据推送。
+- 如果是 `zookeeper` 同步策略,将变更数据更新到 `zookeeper`,而 `ZookeeperSyncCache` 会监听到
`zookeeper` 的数据变更,并予以处理。
+- 如果是 `http` 同步策略,由网关主动发起长轮询请求,默认有 `90s` 超时时间,如果 `shenyu-admin` 没有数据变更,则会阻塞
`http` 请求,如果有数据发生变更则响应变更的数据信息,如果超过 `60s` 仍然没有数据变更则响应空数据,网关层接到响应后,继续发起 `http`
请求,反复同样的请求。
+
+### 3.3 流程分析
+
+流程分析是从源码的角度,展示服务注册流程,数据同步流程和服务调用流程。
+
+#### 3.3.1 服务注册流程
+
+- 读取dubbo服务
+
+使用注解`@ShenyuDubboClient`标记需要注册到网关的`dubbo`服务。
+
+注解扫描通过`ApacheDubboServiceBeanListener`完成,它实现了`ApplicationListener<ContextRefreshedEvent>`接口,在`Spring`容器启动过程中,发生上下文刷新事件时,开始执行事件处理方法`onApplicationEvent()`。在重写的方法逻辑中,读取`Dubbo`服务`ServiceBean`,构建元数据对象和`URI`对象,并向`shenyu-admin`注册。
+
+具体的注册逻辑由注册中心实现,请参考
`[客户端接入原理](https://shenyu.apache.org/zh/docs/design/register-center-design/)` 。
+
+
+- 处理注册信息
+
+客户端通过注册中心注册的元数据和`URI`数据,在`shenyu-admin`端进行处理,负责存储到数据库和同步给`shenyu`网关。`Dubbo`插件的客户端注册处理逻辑在`ShenyuClientRegisterDubboServiceImpl`中。继承关系如下:
+
+
+
+- ShenyuClientRegisterService:客户端注册服务,顶层接口;
+- FallbackShenyuClientRegisterService:注册失败,提供重试操作;
+- AbstractShenyuClientRegisterServiceImpl:抽象类,实现部分公共注册逻辑;
+- ShenyuClientRegisterDubboServiceImpl:实现`Dubbo`插件的注册;
+
+注册信息包括选择器,规则和元数据。
+
+
+整体的`dubbo`服务注册流程如下:
+
+
+
+#### 3.3.2 数据同步流程
+
+- admin更新数据
+
+假设在在后台管理系统中,新增一条选择器数据,请求会进入`SelectorController`类中的`createSelector()`方法,它负责数据的校验,添加或更新数据,返回结果信息。
+
+在`SelectorServiceImpl`类中通过`createOrUpdate()`方法完成数据的转换,保存到数据库,发布事件,更新`upstream`。
+
+在`Service`类完成数据的持久化操作,即保存数据到数据库。发布变更数据通过`eventPublisher.publishEvent()`完成,这个`eventPublisher`对象是一个`ApplicationEventPublisher`类,这个类的全限定名是`org.springframework.context.ApplicationEventPublisher`,发布数据的功能正是是通过`Spring`相关的功能来完成的。
+
+当事件发布完成后,会自动进入到`DataChangedEventDispatcher`类中的`onApplicationEvent()`方法,根据不同数据类型和数据同步方式进行事件处理。
+
+- 网关数据同步
+
+网关在启动时,根据指定的数据同步方式加载不同的配置类,初始化数据同步相关类。
+
+在接收到数据后,进行反序列化操作,读取数据类型和操作类型。不同的数据类型,有不同的数据处理方式,所以有不同的实现类。但是它们之间也有相同的处理逻辑,所以可以通过模板方法设计模式来实现。相同的逻辑放在抽象类`AbstractDataHandler`中的`handle()`方法中,不同逻辑就交给各自的实现类。
+
+新增一条选择器数据,是新增操作,会进入到`SelectorDataHandler.doUpdate()`具体的数据处理逻辑中。
+
+在通用插件数据订阅者`CommonPluginDataSubscriber`,负责处理所有插件、选择器和规则信息
+
+将数据保存到网关的内存中,`BaseDataCache`是最终缓存数据的类,通过单例模式实现。选择器数据就存到了`SELECTOR_MAP`这个`Map`中。在后续使用的时候,也是从这里拿数据。
+
+上述逻辑用流程图表示如下:
+
+
+
+#### 3.3.3 服务调用流程
+
+在`Dubbo`插件体系中,类继承关系如下:
+
+
+
+> ShenyuPlugin:顶层接口,定义接口方法;
+>
+> AbstractShenyuPlugin:抽象类,实现插件共有逻辑;
+>
+>
AbstractDubboPlugin:dubbo插件抽象类,实现`dubbo`共有逻辑(ShenYu网关支持ApacheDubbo和AlibabaDubbo);
+>
+> ApacheDubboPlugin:ApacheDubbo插件。
+
+-
org.apache.shenyu.web.handler.ShenyuWebHandler.DefaultShenyuPluginChain#execute()
+
+通过`ShenYu`网关代理后,请求入口是`ShenyuWebHandler`,它实现了`org.springframework.web.server.WebHandler`接口,通过责任链设计模式将所有插件连接起来。
+
+- org.apache.shenyu.plugin.base.AbstractShenyuPlugin#execute()
+
+当请求到网关时,判断某个插件是否执行,是通过指定的匹配逻辑来完成。在`execute()`方法中执行选择器和规则的匹配逻辑。
+
+
+- org.apache.shenyu.plugin.global.GlobalPlugin#execute()
+
+最先被执行的是`GlobalPlugin` ,它是一个全局插件,在`execute()`方法中构建上下文信息。
+
+- org.apache.shenyu.plugin.base.RpcParamTransformPlugin#execute()
+
+接着被执行的是`RpcParamTransformPlugin` ,
它负责从`http`请求中读取参数,保存到`exchange`中,传递给`rpc`服务。在`execute()`方法中,执行该插件的核心逻辑:从`exchange`中获取请求信息,根据请求传入的内容形式处理参数。
+
+- org.apache.shenyu.plugin.dubbo.common.AbstractDubboPlugin
+
+然后被执行的是`DubboPlugin`
。在`doExecute()`方法中,主要是检查元数据和参数。在`doDubboInvoker()`方法中设置特殊的上下文信息,然后开始`dubbo`的泛化调用。
+
+在`genericInvoker()`方法中:
+
+- 获取`ReferenceConfig`对象;
+- 获取泛化服务`GenericService`对象;
+- 构造请求参数`pair`对象;
+- 发起异步的泛化调用。
+
+通过泛化调用就可以实现在网关调用`dubbo`服务了。
+
+`ReferenceConfig`对象是支持泛化调用的关键对象 ,它的初始化操作是在数据同步的时候完成的。
+
+- org.apache.shenyu.plugin.response.ResponsePlugin#execute()
+
+最后被执行的是`ResponsePlugin` ,它统一处理网关的响应结果信息。处理类型由`MessageWriter`决定,类继承关系如下:
+
+
+
+> MessageWriter:接口,定义消息处理方法;
+>
+> NettyClientMessageWriter:处理`Netty`调用结果;
+>
+> RPCMessageWriter:处理`RPC`调用结果;
+>
+> WebClientMessageWriter:处理`WebClient`调用结果;
+`Dubbo`服务调用,处理结果是`RPCMessageWriter`。
+
+- org.apache.shenyu.plugin.response.strategy.RPCMessageWriter#writeWith()
+
+在`writeWith()`方法中处理响应结果,获取结果或处理异常。
+
+分析至此,关于`Dubbo`插件的源码分析就完成了,分析流程图如下:
+
+
+
+
+## 4. 小结
+
+本文从实际案例出发,由浅入深分析了`ShenYu`网关对Dubbo服务的代理过程。涉及到的主要知识点如下:
+
+- 通过责任链设计模式执行插件;
+- 使用模板方法设计模式实现`AbstractShenyuPlugin`,处理通用的操作类型;
+- 使用单例设计模式实现缓存数据类`BaseDataCache`;
+- 通过`springboot starter`即可引入不同的注册中心和数同步方式,扩展性很好;
+- 通过`admin`支持规则热更新,方便流量管控;
+- `Disruptor`队列是为了数据与操作解耦,以及数据缓冲。
diff --git a/content/zh-cn/overview/what/ecosystem/rate-limit/hystrix.md
b/content/zh-cn/overview/what/ecosystem/rate-limit/hystrix.md
index 166a41bc30..1b6e840162 100644
--- a/content/zh-cn/overview/what/ecosystem/rate-limit/hystrix.md
+++ b/content/zh-cn/overview/what/ecosystem/rate-limit/hystrix.md
@@ -4,4 +4,170 @@ title: "Hystrix"
linkTitle: "Hystrix"
weight: 20
description: ""
----
\ No newline at end of file
+---
+
+## 背景
+
+Hystrix
旨在通过控制那些访问远程系统、服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。Hystrix具备拥有回退机制和断路器功能的线程和信号隔离,请求缓存和请求打包,以及监控和配置等功能。
+
+Dubbo是Alibaba开源的,目前国内最流行的java rpc框架。
+
+本文介绍在spring应用里,怎么把Dubbo和Hystrix结合起来使用。
+
+- <https://github.com/Netflix/Hystrix>
+- <https://github.com/apache/dubbo>
+
+## Spring Boot应用
+
+Demo地址:
<https://github.com/dubbo/dubbo-samples/tree/master/4-governance/dubbo-samples-spring-boot-hystrix>
+
+### 生成dubbo集成spring boot的应用
+
+对于不熟悉dubbo 集成spring boot应用的同学,可以在这里直接生成dubbo + spring boot的工程:
<http://start.dubbo.io/>
+
+### 配置spring-cloud-starter-netflix-hystrix
+
+spring boot官方提供了对hystrix的集成,直接在pom.xml里加入依赖:
+
+```xml
+ <dependency>
+ <groupId>org.springframework.cloud</groupId>
+ <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
+ <version>1.4.4.RELEASE</version>
+ </dependency>
+```
+
+然后在Application类上增加`@EnableHystrix`来启用hystrix starter:
+
+```java
+@SpringBootApplication
+@EnableHystrix
+public class ProviderApplication {
+```
+### 配置Provider端
+在Dubbo的Provider上增加`@HystrixCommand`配置,这样子调用就会经过Hystrix代理。
+```java
+@Service(version = "1.0.0")
+public class HelloServiceImpl implements HelloService {
+ @HystrixCommand(commandProperties = {
+ @HystrixProperty(name =
"circuitBreaker.requestVolumeThreshold", value = "10"),
+ @HystrixProperty(name =
"execution.isolation.thread.timeoutInMilliseconds", value = "2000") })
+ @Override
+ public String sayHello(String name) {
+ // System.out.println("async provider received: " + name);
+ // return "annotation: hello, " + name;
+ throw new RuntimeException("Exception to show hystrix enabled.");
+ }
+}
+```
+### 配置Consumer端
+对于Consumer端,则可以增加一层method调用,并在method上配置`@HystrixCommand`。当调用出错时,会走到`fallbackMethod
= "reliable"`的调用里。
+```java
+ @Reference(version = "1.0.0")
+ private HelloService demoService;
+ @HystrixCommand(fallbackMethod = "reliable")
+ public String doSayHello(String name) {
+ return demoService.sayHello(name);
+ }
+ public String reliable(String name) {
+ return "hystrix fallback value";
+ }
+```
+通过上面的配置,很简单地就完成了Spring Boot里Dubbo + Hystrix的集成。
+## 传统Spring Annotation应用
+Demo地址:
<https://github.com/dubbo/dubbo-samples/tree/master/4-governance/dubbo-samples-spring-hystrix>
+传统spring annotation应用的配置其实也很简单,和spring boot应用不同的是:
+1. 显式配置Spring AOP支持:`@EnableAspectJAutoProxy`
+2. 显式通过`@Configuration`配置`HystrixCommandAspect` Bean。
+```java
+ @Configuration
+ @EnableDubbo(scanBasePackages =
"com.alibaba.dubbo.samples.annotation.action")
+ @PropertySource("classpath:/spring/dubbo-consumer.properties")
+ @ComponentScan(value = {"com.alibaba.dubbo.samples.annotation.action"})
+ @EnableAspectJAutoProxy
+ static public class ConsumerConfiguration {
+ @Bean
+ public HystrixCommandAspect hystrixCommandAspect() {
+ return new HystrixCommandAspect();
+ }
+ }
+```
+## Hystrix集成Spring AOP原理
+在上面的例子里可以看到,Hystrix对Spring的集成是通过Spring AOP来实现的。下面简单分析下实现。
+```java
+@Aspect
+public class HystrixCommandAspect {
+
@Pointcut("@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand)")
+ public void hystrixCommandAnnotationPointcut() {
+ }
+
@Pointcut("@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser)")
+ public void hystrixCollapserAnnotationPointcut() {
+ }
+ @Around("hystrixCommandAnnotationPointcut() ||
hystrixCollapserAnnotationPointcut()")
+ public Object methodsAnnotatedWithHystrixCommand(final ProceedingJoinPoint
joinPoint) throws Throwable {
+ Method method = getMethodFromTarget(joinPoint);
+ Validate.notNull(method, "failed to get method from joinPoint: %s",
joinPoint);
+ if (method.isAnnotationPresent(HystrixCommand.class) &&
method.isAnnotationPresent(HystrixCollapser.class)) {
+ throw new IllegalStateException("method cannot be annotated with
HystrixCommand and HystrixCollapser " +
+ "annotations at the same time");
+ }
+ MetaHolderFactory metaHolderFactory =
META_HOLDER_FACTORY_MAP.get(HystrixPointcutType.of(method));
+ MetaHolder metaHolder = metaHolderFactory.create(joinPoint);
+ HystrixInvokable invokable =
HystrixCommandFactory.getInstance().create(metaHolder);
+ ExecutionType executionType =
metaHolder.isCollapserAnnotationPresent() ?
+ metaHolder.getCollapserExecutionType() :
metaHolder.getExecutionType();
+ Object result;
+ try {
+ if (!metaHolder.isObservable()) {
+ result = CommandExecutor.execute(invokable, executionType,
metaHolder);
+ } else {
+ result = executeObservable(invokable, executionType,
metaHolder);
+ }
+ } catch (HystrixBadRequestException e) {
+ throw e.getCause() != null ? e.getCause() : e;
+ } catch (HystrixRuntimeException e) {
+ throw hystrixRuntimeExceptionToThrowable(metaHolder, e);
+ }
+ return result;
+ }
+```
+1. `HystrixCommandAspect`里定义了两个注解的AspectJ Pointcut:`@HystrixCommand`,
`@HystrixCollapser`。所有带这两个注解的spring bean都会经过AOP处理
+2. 在`@Around`
AOP处理函数里,可以看到Hystrix会创建出`HystrixInvokable`,再通过`CommandExecutor`来执行
+## spring-cloud-starter-netflix-hystrix的代码分析
+1. `@EnableHystrix`
引入了`@EnableCircuitBreaker`,`@EnableCircuitBreaker`引入了`EnableCircuitBreakerImportSelector`
+ ```java
+ @EnableCircuitBreaker
+ public @interface EnableHystrix {
+ }
+
+ @Import(EnableCircuitBreakerImportSelector.class)
+ public @interface EnableCircuitBreaker {
+ }
+ ```
+2.
`EnableCircuitBreakerImportSelector`继承了`SpringFactoryImportSelector<EnableCircuitBreaker>`,使spring加载`META-INF/spring.factories`里的`EnableCircuitBreaker`声明的配置
+
在`META-INF/spring.factories`里可以找到下面的配置,也就是引入了`HystrixCircuitBreakerConfiguration`。
+ ```properties
+ org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker=\
+ org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerConfiguration
+ ```
+3. 在`HystrixCircuitBreakerConfiguration`里可以发现创建了`HystrixCommandAspect`
+ ```java
+ @Configuration
+ public class HystrixCircuitBreakerConfiguration {
+
+ @Bean
+ public HystrixCommandAspect hystrixCommandAspect() {
+ return new HystrixCommandAspect();
+ }
+ ```
+可见`spring-cloud-starter-netflix-hystrix`实际上也是创建了`HystrixCommandAspect`来集成Hystrix。
+另外`spring-cloud-starter-netflix-hystrix`里还有metrics, health, dashboard等集成。
+## 总结
+- 对于dubbo provider的`@Service`是一个spring bean,直接在上面配置`@HystrixCommand`即可
+- 对于dubbo consumer的`@Reference`,可以通过加一层简单的spring method包装,配置`@HystrixCommand`即可
+- Hystrix本身提供`HystrixCommandAspect`来集成Spring
AOP,配置了`@HystrixCommand`和`@HystrixCollapser`的spring method都会被Hystrix处理
+## 链接
+- <https://github.com/Netflix/Hystrix>
+- <https://github.com/apache/dubbo>
+- <http://start.dubbo.io/>
+-
<https://cloud.spring.io/spring-cloud-netflix/single/spring-cloud-netflix.html#_circuit_breaker_hystrix_clients>
diff --git a/content/zh-cn/overview/what/ecosystem/rate-limit/sentinel.md
b/content/zh-cn/overview/what/ecosystem/rate-limit/sentinel.md
index 026c8055c8..89d3bc5ef9 100644
--- a/content/zh-cn/overview/what/ecosystem/rate-limit/sentinel.md
+++ b/content/zh-cn/overview/what/ecosystem/rate-limit/sentinel.md
@@ -4,4 +4,133 @@ title: "Sentinel"
linkTitle: "Sentinel"
weight: 10
description: ""
----
\ No newline at end of file
+---
+
+在复杂的生产环境下可能部署着成千上万的 Dubbo
服务实例,流量持续不断地进入,服务之间进行相互调用。但是分布式系统中可能会因流量激增、系统负载过高、网络延迟等一系列问题,导致某些服务不可用,如果不进行相应的控制可能导致级联故障,影响服务的可用性,因此如何对流量进行合理的控制,成为保障服务稳定性的关键。
+
+[Sentinel](https://github.com/alibaba/Sentinel)
是阿里中间件团队开源的,面向分布式服务架构的轻量级流量控制产品,主要以流量为切入点,从**流量控制**、**熔断降级**、**系统负载保护**等多个维度来帮助用户保护服务的稳定性。本文将基于
Dubbo,看看 Sentinel 是如何进行流量控制的,并且提供 Dubbo 整合 Sentinel 的最佳实践。
+
+## 快速接入 Sentinel
+
+Sentinel 意为**哨兵**,这个命名形象的诠释了 Sentinel 在分布式系统中的工作角色和重要性。以 Sentinel 在 Dubbo
生态系统中的作用为例,Dubbo 的核心模块包括注册中心、服务提供方、服务消费方(服务调用方)和监控四个模块。Sentinel
通过对服务提供方和服务消费方的限流来进一步提升服务的可用性。接下来我们看看 Sentinel 对服务提供方和服务消费方限流的技术实现方式。
+
+
+
+Sentinel 提供了与 Dubbo 适配的模块 – [Sentinel Dubbo
Adapter](https://github.com/dubbo/dubbo-sentinel-support),包括针对服务提供方的过滤器和服务消费方的过滤器(Filter)。使用时我们只需引入以下模块(以
Maven 为例):
+
+```xml
+<dependency>
+ <groupId>com.alibaba.csp</groupId>
+ <artifactId>sentinel-dubbo-adapter</artifactId>
+ <version>x.y.z</version>
+</dependency>
+```
+
+引入此依赖后,Dubbo 的服务接口和方法(包括调用端和服务端)就会成为 Sentinel 中的资源,在配置了规则后就可以自动享受到 Sentinel
的防护能力。同时提供了灵活的配置选项,例如若不希望开启 Sentinel Dubbo Adapter 中的某个 Filter,可以手动关闭对应的 Filter。
+
+接入 Sentinel Dubbo Adapter 后,即使未配置规则,Sentinel 也会对相应的 Dubbo 服务的调用信息进行统计。那么我们怎么知道
Sentinel 接入成功了呢?这时候就要请出一大利器 —— Sentinel 控制台了。
+
+## 限流必备 - 监控管理
+
+流量具有很强的实时性,之所以需要限流,是因为我们无法对流量的到来作出精确的预判,不然的话我们完全可以通过弹性的计算资源来处理,所以这时候为了保证限流的准确性,限流框架的监控功能就非常重要了。
+
+Sentinel 的控制台(Dashboard)是流量控制、熔断降级规则统一配置和管理的入口,同时它为用户提供了多个维度的监控功能。在 Sentinel
控制台上,我们可以配置规则并实时查看流量控制效果。
+
+接入 Sentinel 控制台的步骤如下(**缺一不可**):
+
+1. 按照 [Sentinel
控制台文档](https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0)
启动控制台
+2. 应用引入 `sentinel-transport-simple-http` 依赖,以便控制台可以拉取对应应用的相关信息
+3. 给应用添加相关的启动参数,启动应用。需要配置的参数有:
+ - `-Dcsp.sentinel.api.port`:客户端的 port,用于上报相关信息(默认为 8719)
+ - `-Dcsp.sentinel.dashboard.server`:控制台的地址
+ - `-Dproject.name`:应用名称,会在控制台中显示
+
+注意某些环境下本地运行 Dubbo 服务还需要加上 `-Djava.net.preferIPv4Stack=true` 参数。比如中 Service
Provider 的启动参数可以配成:
+
+```bash
+-Djava.net.preferIPv4Stack=true -Dcsp.sentinel.api.port=8720
-Dcsp.sentinel.dashboard.server=localhost:8080
-Dproject.name=dubbo-provider-demo
+```
+
+这样在启动应用后就能在控制台找到对应的应用了。以下是常用功能:
+
+- **单台设备监控**:当在机器列表中看到您的机器,就代表着已经成功接入控制台,可以查看单台设备的设备名称、IP地址、端口号、健康状态和心跳时间等信息。
+
+
+
+-
**链路监控**:簇点链路实时的去拉取指定客户端资源的运行情况,它提供了两种展示模式,一种用书状结构展示资源的调用链路;另外一种则不区分调用链路展示资源的运行情况。通过链路监控,可以查看到每个资源的流控和降级的历史状态。
+
+| 树状链路| 平铺链路
+| :----: | :----
+||
+
+- **聚合监控**:同一个服务下的所有机器的簇点信息会被汇总,实现实时监控,精确度达秒级。
+
+
+
+- **规则配置**:可以查看已有的限流、降级和系统保护规则,并实时地进行配置。
+
+
+
+## Sentinel 基于 Dubbo 的最佳实践
+
+> 具体 Demo 代码请见
[sentinel-demo-dubbo](https://github.com/alibaba/Sentinel/tree/master/sentinel-demo/sentinel-demo-dubbo)。
+### Service Provider
+
+> 对服务提供方的流量控制可分为**服务提供方的自我保护能力**和**服务提供方对服务消费方的请求分配能力**两个维度。
+>
+Service Provider 用于向外界提供服务,处理各个消费者的调用请求。为了保护 Provider 不被激增的流量拖垮影响稳定性,可以给
Provider 配置 **QPS 模式**的限流,这样当每秒的请求量超过设定的阈值时会自动拒绝多的请求。限流粒度可以是 *服务接口* 和 *服务方法*
两种粒度。若希望整个服务接口的 QPS 不超过一定数值,则可以为对应服务接口资源(resourceName 为**接口全限定名**)配置 QPS
阈值;若希望服务的某个方法的 QPS 不超过一定数值,则可以为对应服务方法资源(resourceName 为**接口全限定名:方法签名**)配置 QPS
阈值。有关配置详情请参考 [流量控制 |
Sentinel](https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6)。
+
+我们看一下这种模式的限流产生的效果。假设我们已经定义了某个服务接口
`com.alibaba.csp.sentinel.demo.dubbo.FooService`,其中有一个方法
`sayHello(java.lang.String)`,Provider 端该方法设定 QPS 阈值为 10。在 Consumer 端在 1s 之内连续发起
15 次调用,可以通过日志文件看到 Provider 端被限流。拦截日志统一记录在 `~/logs/csp/sentinel-block.log` 中:
+
+```
+2018-07-24
17:13:43|1|com.alibaba.csp.sentinel.demo.dubbo.FooService:sayHello(java.lang.String),FlowException,default,|5,0
+```
+
+在 Provider 对应的 metrics 日志中也有记录:
+
+```
+1532423623000|2018-07-24
17:13:43|com.alibaba.csp.sentinel.demo.dubbo.FooService|15|0|15|0|3
+1532423623000|2018-07-24
17:13:43|com.alibaba.csp.sentinel.demo.dubbo.FooService:sayHello(java.lang.String)|10|5|10|0|0
+```
+
+根据**调用方**的需求来分配服务提供方的处理能力也是常见的限流方式。比如有两个服务 A 和 B 都向 Service Provider
发起调用请求,我们希望只对来自服务 B 的请求进行限流,则可以设置限流规则的 `limitApp` 为服务 B 的名称。Sentinel Dubbo
Adapter 会自动解析 Dubbo 消费者(调用方)的 application name
作为调用方名称(`origin`),在进行资源保护的时候都会带上调用方名称。若限流规则未配置调用方(`default`),则该限流规则对所有调用方生效。若限流规则配置了调用方则限流规则将仅对指定调用方生效。
+
+> 注:Dubbo 默认通信不携带对端 application name 信息,因此需要开发者在调用端手动将 application name 置入
attachment 中,provider 端进行相应的解析。Sentinel Dubbo Adapter 实现了一个 Filter 用于自动从
consumer 端向 provider 端透传 application name。若调用端未引入 Sentinel Dubbo
Adapter,又希望根据调用端限流,可以在调用端手动将 application name 置入 attachment 中,key 为
`dubboApplication`。
+在限流日志中会也会记录调用方的名称,如下面的日志中的 `demo-consumer` 即为调用方名称:
+
+```
+2018-07-25
16:26:48|1|com.alibaba.csp.sentinel.demo.dubbo.FooService:sayHello(java.lang.String),FlowException,default,demo-consumer|5,0
+```
+
+### Service Consumer
+
+> 对服务提供方的流量控制可分为**控制并发线程数**和**服务降级**两个维度。
+#### 并发线程数限流
+
+Service Consumer 作为客户端去调用远程服务。每一个服务都可能会依赖几个下游服务,若某个服务 A 依赖的下游服务 B 出现了不稳定的情况,服务
A 请求 服务 B 的响应时间变长,从而服务 A 调用服务 B 的线程就会产生堆积,最终可能耗尽服务 A 的线程数。我们通过用并发线程数来控制对下游服务 B
的访问,来保证下游服务不可靠的时候,不会拖垮服务自身。基于这种场景,推荐给 Consumer
配置**线程数模式**的限流,来保证自身不被不稳定服务所影响。采用基于线程数的限流模式后,我们不需要再显式地去进行线程池隔离,Sentinel
会控制资源的线程数,超出的请求直接拒绝,直到堆积的线程处理完成,可以达到**信号量隔离**的效果。
+
+我们看一下这种模式的效果。假设当前服务 A 依赖两个远程服务方法 `sayHello(java.lang.String)` 和
`doAnother()`。前者远程调用的响应时间 为 1s-1.5s 之间,后者 RT 非常小(30 ms 左右)。服务 A 端设两个远程方法 thread
count 为 5。然后每隔 50 ms 左右向线程池投入两个任务,作为消费者分别远程调用对应方法,持续 10 次。可以看到 `sayHello` 方法被限流
5 次,因为后面调用的时候前面的远程调用还未返回(RT 高);而 `doAnother()`
调用则不受影响。线程数目超出时快速失败能够有效地防止自己被慢调用所影响。
+
+#### 服务降级
+
+当服务依赖于多个下游服务,而某个下游服务调用非常慢时,会严重影响当前服务的调用。这里我们可以利用 Sentinel 熔断降级的功能,为调用端配置基于平均
RT
的[降级规则](https://github.com/alibaba/Sentinel/wiki/%E7%86%94%E6%96%AD%E9%99%8D%E7%BA%A7)。这样当调用链路中某个服务调用的平均
RT 升高,在一定的次数内超过配置的 RT 阈值,Sentinel
就会对此调用资源进行降级操作,接下来的调用都会立刻拒绝,直到过了一段设定的时间后才恢复,从而保护服务不被调用端短板所影响。同时可以配合 fallback
功能使用,在被降级的时候提供相应的处理逻辑。
+
+### Fallback
+
+从 0.1.1 版本开始,Sentinel Dubbo Adapter 还支持配置全局的 fallback 函数,可以在 Dubbo
服务被限流/降级/负载保护的时候进行相应的 fallback 处理。用户只需要实现自定义的
[`DubboFallback`](https://github.com/alibaba/Sentinel/blob/master/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallback.java)
接口,并通过 `DubboFallbackRegistry` 注册即可。默认情况会直接将 `BlockException` 包装后抛出。同时,我们还可以配合
[Dubbo 的 fallback 机制](/zh-cn/docsv2.7/user/examples/local-mock/) 来为降级的服务提供替代的实现。
+
+## Sentinel 与 Hystrix 的比较
+
+目前业界常用的熔断降级/隔离的库是 Netflix 的 [Hystrix](https://github.com/Netflix/Hystrix),那么
Sentinel 与 Hystrix 有什么异同呢?Hystrix 的关注点在于以 *隔离* 和 *熔断* 为主的容错机制,而 Sentinel
的侧重点在于多样化的流量控制、熔断降级、系统负载保护、实时监控和控制台,可以看到解决的问题还是有比较大的不同的。
+
+Hystrix 采用命令模式封装资源调用逻辑,并且资源的定义与隔离规则是强依赖的,即在创建 HystrixCommand
的时候就要指定隔离规则(因其执行模型依赖于隔离模式)。Sentinel
的设计更为简单,不关注资源是如何执行的,资源的定义与规则的配置相分离。用户可以先定义好资源,然后在需要的时候配置规则即可。Sentinel
的原则非常简单:根据对应资源配置的规则来为资源执行相应的限流/降级/负载保护策略,若规则未配置则仅进行统计。从 0.1.1 版本开始,Sentinel
还引入了[注解支持](https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81),可以更方便地定义资源。
+
+隔离是 Hystrix 的核心功能。Hystrix 通过线程池或信号量的方式来对依赖(即 Sentinel
中对应的资源)进行隔离,其中最常用的是资源隔离。Hystrix
线程池隔离的好处是比较彻底,但是不足之处在于要开很多线程池,在应用本身线程数目比较多的时候上下文切换的 overhead 会非常大;Hystrix
的信号量隔离模式可以限制调用的并发数而不显式创建线程,这样的方式比较轻量级,但缺点是无法对慢调用自动进行降级,只能等待客户端自己超时,因此仍然可能会出现级联阻塞的情况。Sentinel
可以通过并发线程数模式的流量控制来提供信号量隔离的功能。并且结合基于响应时间的熔断降级模式,可以在不稳定资源的平均响应时间比较高的时候自动降级,防止过多的慢调用占满并发数,影响整个系统。
+
+Hystrix 熔断降级功能采用熔断器模式,在某个服务失败比率高时自动进行熔断。Sentinel
的熔断降级功能更为通用,支持平均响应时间与失败比率两个指标。Sentinel
还提供各种调用链路关系和流量控制效果支持,同时还可以根据系统负载去实时地调整流量来保护系统,应用场景更为丰富。同时,Sentinel 还提供了实时的监控
API 和控制台,可以方便用户快速了解目前系统的状态,对服务的稳定性了如指掌。
+
+更详细的对比请参见 [Sentinel 与 Hystrix
的对比](https://github.com/alibaba/Sentinel/wiki/Sentinel-%E4%B8%8E-Hystrix-%E7%9A%84%E5%AF%B9%E6%AF%94)。
+
+## 总结
+
+以上介绍的只是 Sentinel 的一个最简单的场景 —— 限流。Sentinel 还可以处理更复杂的各种情况,比如超时熔断、冷启动、请求匀速等。可以参考
[Sentinel
文档](https://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E9%A1%B5),更多的场景等待你去挖掘!
diff --git a/content/zh-cn/overview/what/ecosystem/transaction/seata.md
b/content/zh-cn/overview/what/ecosystem/transaction/seata.md
index 86e33da93a..3699c524a5 100644
--- a/content/zh-cn/overview/what/ecosystem/transaction/seata.md
+++ b/content/zh-cn/overview/what/ecosystem/transaction/seata.md
@@ -4,4 +4,210 @@ title: "Seata"
linkTitle: "Seata"
weight: 10
description: ""
----
\ No newline at end of file
+---
+
+## 案例
+
+用户采购商品业务,整个业务包含3个微服务:
+
+- 库存服务: 扣减给定商品的库存数量。
+- 订单服务: 根据采购请求生成订单。
+- 账户服务: 用户账户金额扣减。
+
+### 业务结构图
+
+
+
+
+### StorageService
+
+```java
+public interface StorageService {
+ /**
+ * 扣除存储数量
+ */
+ void deduct(String commodityCode, int count);
+}
+```
+
+### OrderService
+
+```java
+public interface OrderService {
+ /**
+ * 创建订单
+ */
+ Order create(String userId, String commodityCode, int orderCount);
+}
+```
+
+### AccountService
+
+```java
+public interface AccountService {
+ /**
+ * 从用户账户中借出
+ */
+ void debit(String userId, int money);
+}
+```
+
+### 主要的业务逻辑:
+
+```java
+public class BusinessServiceImpl implements BusinessService {
+ private StorageService storageService;
+ private OrderService orderService;
+ /**
+ * 采购
+ */
+ public void purchase(String userId, String commodityCode, int orderCount) {
+ storageService.deduct(commodityCode, orderCount);
+ orderService.create(userId, commodityCode, orderCount);
+ }
+}
+```
+
+```java
+public class StorageServiceImpl implements StorageService {
+ private StorageDAO storageDAO;
+
+ @Override
+ public void deduct(String commodityCode, int count) {
+ Storage storage = new Storage();
+ storage.setCount(count);
+ storage.setCommodityCode(commodityCode);
+ storageDAO.update(storage);
+ }
+}
+```
+
+```java
+public class OrderServiceImpl implements OrderService {
+ private OrderDAO orderDAO;
+ private AccountService accountService;
+ public Order create(String userId, String commodityCode, int orderCount) {
+ int orderMoney = calculate(commodityCode, orderCount);
+ accountService.debit(userId, orderMoney);
+ Order order = new Order();
+ order.userId = userId;
+ order.commodityCode = commodityCode;
+ order.count = orderCount;
+ order.money = orderMoney;
+ return orderDAO.insert(order);
+ }
+}
+```
+
+## Seata 分布式事务解决方案
+
+
+
+此处仅仅需要一行注解 `@GlobalTransactional` 写在业务发起方的方法上:
+
+```java
+ @GlobalTransactional
+ public void purchase(String userId, String commodityCode, int orderCount) {
+ ......
+ }
+```
+
+## Dubbo 与 Seata 结合的例子
+
+### Step 1: 安装数据库
+
+- 要求: MySQL (InnoDB 存储引擎)。
+
+**提示:** 事实上例子中3个微服务需要3个独立的数据库,但为了方便我们使用同一物理库并配置3个逻辑连接串。
+
+更改以下xml文件中的数据库url、username和password
+
+dubbo-account-service.xml
+dubbo-order-service.xml
+dubbo-storage-service.xml
+
+```xml
+ <property name="url" value="jdbc:mysql://x.x.x.x:3306/xxx" />
+ <property name="username" value="xxx" />
+ <property name="password" value="xxx" />
+```
+### Step 2: 为 Seata 创建 undo_log 表
+
+`UNDO_LOG` 此表用于 Seata 的AT模式。
+
+```sql
+-- 注意当 Seata 版本升级至 0.3.0+ 将由之前的普通索引变更为唯一索引。
+CREATE TABLE `undo_log` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `branch_id` bigint(20) NOT NULL,
+ `xid` varchar(100) NOT NULL,
+ `context` varchar(128) NOT NULL,
+ `rollback_info` longblob NOT NULL,
+ `log_status` int(11) NOT NULL,
+ `log_created` datetime NOT NULL,
+ `log_modified` datetime NOT NULL,
+ `ext` varchar(100) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
+```
+
+### Step 3: 创建相关业务表
+
+```sql
+DROP TABLE IF EXISTS `storage_tbl`;
+CREATE TABLE `storage_tbl` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `commodity_code` varchar(255) DEFAULT NULL,
+ `count` int(11) DEFAULT 0,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY (`commodity_code`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+DROP TABLE IF EXISTS `order_tbl`;
+CREATE TABLE `order_tbl` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `user_id` varchar(255) DEFAULT NULL,
+ `commodity_code` varchar(255) DEFAULT NULL,
+ `count` int(11) DEFAULT 0,
+ `money` int(11) DEFAULT 0,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+DROP TABLE IF EXISTS `account_tbl`;
+CREATE TABLE `account_tbl` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `user_id` varchar(255) DEFAULT NULL,
+ `money` int(11) DEFAULT 0,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+```
+### Step 4: 启动 Seata-Server 服务
+
+- 下载[服务器软件包](https://github.com/seata/seata/releases),将其解压缩。
+
+```shell
+Usage: sh seata-server.sh(for linux and mac) or cmd seata-server.bat(for
windows) [options]
+ Options:
+ --host, -h
+ The host to bind.
+ Default: 0.0.0.0
+ --port, -p
+ The port to listen.
+ Default: 8091
+ --storeMode, -m
+ log store mode : file、db
+ Default: file
+ --help
+e.g.
+sh seata-server.sh -p 8091 -h 127.0.0.1 -m file
+```
+
+### Step 5: 运行例子
+
+- 启动账户服务
([DubboAccountServiceStarter](https://github.com/apache/dubbo-samples/blob/master/99-integration/dubbo-samples-transaction/src/main/java/org/apache/dubbo/samples/starter/DubboAccountServiceStarter.java)).
+- 启动库存服务
([DubboStorageServiceStarter](https://github.com/apache/dubbo-samples/blob/master/99-integration/dubbo-samples-transaction/src/main/java/org/apache/dubbo/samples/starter/DubboStorageServiceStarter.java)).
+- 启动订单服务
([DubboOrderServiceStarter](https://github.com/apache/dubbo-samples/blob/master/99-integration/dubbo-samples-transaction/src/main/java/org/apache/dubbo/samples/starter/DubboOrderServiceStarter.java)).
+- 运行BusinessService入口
([DubboBusinessTester](https://github.com/apache/dubbo-samples/blob/master/99-integration/dubbo-samples-transaction/src/main/java/org/apache/dubbo/samples/starter/DubboBusinessTester.java)).
+
+### 相关项目
+* Seata: https://github.com/seata/seata
+* Seata Samples : https://github.com/seata/seata-samples