You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by ni...@apache.org on 2019/09/30 10:10:29 UTC
[servicecomb-pack] 32/42: SCB-1368 Updated document
This is an automated email from the ASF dual-hosted git repository.
ningjiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/servicecomb-pack.git
commit 24fd1bf00c9299036f95686bce65aabecd466a9c
Author: Lei Zhang <co...@gmail.com>
AuthorDate: Fri Sep 27 17:07:08 2019 +0800
SCB-1368 Updated document
---
.../src/main/resources/application.yaml | 4 +-
docs/fsm/akka_zh.md | 69 +++
docs/fsm/apis_zh.md | 132 ++++++
docs/fsm/assets/alpha-cluster-architecture.png | Bin 0 -> 537656 bytes
docs/fsm/eventchannel_zh.md | 34 ++
docs/fsm/{fsm_manual_zh.md => fsm_manual.md} | 176 ++++---
docs/fsm/fsm_manual_zh.md | 176 ++++---
docs/fsm/how_to_use_fsm.md | 522 --------------------
docs/fsm/how_to_use_fsm_zh.md | 527 ---------------------
docs/fsm/persistence_zh.md | 235 +++++++++
docs/user_guide.md | 2 +-
docs/user_guide_zh.md | 2 +-
12 files changed, 696 insertions(+), 1183 deletions(-)
diff --git a/alpha/alpha-server/src/main/resources/application.yaml b/alpha/alpha-server/src/main/resources/application.yaml
index 05bfc20..e0d5b23 100644
--- a/alpha/alpha-server/src/main/resources/application.yaml
+++ b/alpha/alpha-server/src/main/resources/application.yaml
@@ -68,10 +68,10 @@ akkaConfig:
persistence:
journal:
plugin: akka.persistence.journal.inmem
- leveldb.dir: target/example/journal
+ leveldb.dir: actor/persistence/journal
snapshot-store:
plugin: akka.persistence.snapshot-store.local
- local.dir: target/example/snapshots
+ local.dir: actor/persistence/snapshots
remote:
watch-failure-detector:
acceptable-heartbeat-pause: 6s
diff --git a/docs/fsm/akka_zh.md b/docs/fsm/akka_zh.md
new file mode 100644
index 0000000..d51384e
--- /dev/null
+++ b/docs/fsm/akka_zh.md
@@ -0,0 +1,69 @@
+# Akka 配置
+
+Alpha 已经定义了一些 Akka 的参数,如果要在外部修改,可以通过 `akkaConfig.{akka key} = value` 方式配置
+
+* Alpha 单例模式
+
+ 状态机持久化参数
+
+ ```properties
+ akkaConfig:
+ akka:
+ persistence:
+ journal:
+ plugin: akka.persistence.journal.inmem
+ leveldb.dir: actor/persistence/journal
+ snapshot-store:
+ plugin: akka.persistence.snapshot-store.local
+ local.dir: actor/persistence/snapshots
+ ```
+
+* Alpha 集群模式
+
+ 状态机持久化参数
+
+ ```properties
+ akkaConfig:
+ akka:
+ actor:
+ provider: cluster
+ persistence:
+ at-least-once-delivery:
+ redeliver-interval: 10s
+ redelivery-burst-limit: 2000
+ journal:
+ plugin: akka-persistence-redis.journal
+ snapshot-store:
+ plugin: akka-persistence-redis.snapshot
+ akka-persistence-redis:
+ redis:
+ mode: "simple"
+ host: "127.0.0.1"
+ port: 6379
+ database: 0
+ ```
+
+ Kafka 消费者参数
+
+ ```properties
+ akkaConfig:
+ akka:
+ kafka:
+ consumer:
+ poll-interval: 50ms
+ stop-timeout: 30s
+ close-timeout: 20s
+ commit-timeout: 15s
+ commit-time-warning: 5s
+ commit-refresh-interval: infinite
+ wait-close-partition: 500ms
+ position-timeout: 10s
+ offset-for-times-timeout: 10s
+ metadata-request-timeout: 10s
+ eos-draining-check-interval: 30ms
+ partition-handler-warning: 5s
+ connection-checker.enable: false
+ connection-checker.max-retries: 3
+ connection-checker.check-interval: 15s
+ connection-checker.backoff-factor: 2.0
+ ```
\ No newline at end of file
diff --git a/docs/fsm/apis_zh.md b/docs/fsm/apis_zh.md
new file mode 100644
index 0000000..dab3577
--- /dev/null
+++ b/docs/fsm/apis_zh.md
@@ -0,0 +1,132 @@
+# APIs
+
+#### 性能度量
+
+你可以使用 API 查询 Alpha 的性能指标,你可以使用基准测试工具 `AlphaBenchmark` 模拟发送数据后快速体验这一功能
+
+例如:使用以下命令模拟 10 并发,发送 1000 个全局事务
+
+```bash
+java -jar alpha-benchmark-0.5.0-SNAPSHOT-exec.jar --alpha.cluster.address=0.0.0.0:8080 --w=0 --n=1000 --c=10
+```
+
+查询性能指标
+
+```bash
+curl http://localhost:8090/alpha/api/v1/metrics
+{
+ nodeType: "MASTER",
+ metrics: {
+ eventReceived: 8000,
+ eventAccepted: 8000,
+ eventRejected: 0,
+ eventAvgTime: 0,
+ actorReceived: 8000,
+ actorAccepted: 8000,
+ actorRejected: 0,
+ actorAvgTime: 0,
+ sagaBeginCounter: 1000,
+ sagaEndCounter: 1000,
+ sagaAvgTime: 9,
+ committed: 1000,
+ compensated: 0,
+ suspended: 0,
+ repositoryReceived: 1000,
+ repositoryAccepted: 1000,
+ repositoryRejected: 0,
+ repositoryAvgTime: 0.88
+ }
+}
+```
+
+例如以上指标中显示 `sagaAvgTime: 9` 表示每个全局事务在Akka的处理耗时9毫秒,`repositoryAvgTime: 0.88` 表示每个全局事务入库耗时0.88毫秒
+
+指标说明
+
+- eventReceived: Alpha 收到的 gRPC 事件数量
+- eventAccepted: Alpha 处理的 gRPC 事件数量(事件放入事件通道)
+- eventRejected: Alpha 拒绝的 gRPC 事件数量
+- eventAvgTime: Alpha 平均耗时(毫秒)
+- actorReceived: Akka 收到的事件数量
+- actorAccepted: Akka 处理的事件数量
+- actorRejected: Akka 拒绝的事件数量
+- actorAvgTime: Akka 平均耗时(毫秒)
+- sagaBeginCounter: 开始的 Saga 全局事务数量
+- sagaEndCounter: 结束的 Saga 全局事务数量
+- sagaAvgTime: 平均耗时(毫秒)
+- committed: COMMITTED状态的 Saga 全局事务数量
+- compensated: COMPENSATED状态的 Saga 全局事务数量
+- suspended: SUSPENDED状态的 Saga 的全局事务数量
+- repositoryReceived: 存储模块收到的全局事务数量
+- repositoryAccepted: 存储模块处理的全局事务数量
+- repositoryRejected: 存储模块拒绝的全局事务数量
+- repositoryAvgTime: 平均耗时(毫秒)
+
+#### 事务数据查询
+
+> 需要启动 Elasticsearch 存储事务
+
+- 查询事务列表
+
+ ```bash
+ curl -X GET http://localhost:8090/alpha/api/v1/transaction?page=0&size=50
+
+ {
+ "total": 2002,
+ "page": 0,
+ "size": 50,
+ "elapsed": 581,
+ "globalTransactions": [...]
+ }
+ ```
+
+ 请求参数
+
+ - page 页号
+ - size 返回行数
+
+ 返回参数
+
+ - total 总行数
+ - page 本次查询结果页号
+ - size 本次查询行数
+ - elapsed 本次查询耗时(毫秒)
+ - globalTransactions 事件数据列表
+
+- 查询一条事务
+
+ ```bash
+ curl -X GET http://localhost:8090/alpha/api/v1/transaction/{globalTxId}
+
+ {
+ "globalTxId": "e00a3bac-de6b-498f-99a4-c11d3087fd14",
+ "type": "SAGA",
+ "serviceName": "alpha-benchmark",
+ "instanceId": "alpha-benchmark-127.0.0.1",
+ "beginTime": 1564762932963,
+ "endTime": 1564762933197,
+ "state": "COMMITTED",
+ "subTxSize": 3,
+ "durationTime": 408,
+ "subTransactions": [...],
+ "events": [...]
+ }
+ ```
+
+ 请求参数
+
+ - globalTxId 全局事务ID
+
+ 返回参数
+
+ - globalTxId 全局事务ID
+ - type 事务类型,目前只有SAGA,后期增加TCC
+ - serviceName 全局事务发起方服务名称
+ - instanceId 全局事务发起方实例ID
+ - beginTime 事务开始时间
+ - endTime 事务结束时间
+ - state 事务最终状态
+ - subTxSize 包含子事务数量
+ - durationTime 全局事务处理耗时
+ - subTransactions 子事务数据列表
+ - events 事件列表
\ No newline at end of file
diff --git a/docs/fsm/assets/alpha-cluster-architecture.png b/docs/fsm/assets/alpha-cluster-architecture.png
new file mode 100644
index 0000000..1b3c2e5
Binary files /dev/null and b/docs/fsm/assets/alpha-cluster-architecture.png differ
diff --git a/docs/fsm/eventchannel_zh.md b/docs/fsm/eventchannel_zh.md
new file mode 100644
index 0000000..3fcabcd
--- /dev/null
+++ b/docs/fsm/eventchannel_zh.md
@@ -0,0 +1,34 @@
+# 事件通道
+
+Alpha 收到 Omeag 发送的事件后放入事件通道等待 Akka 处理,事件通道有两种实现方式,一种是内存通道另一种是 Kafka 通道
+
+| 通道类型 | 模式 | 说明 |
+| -------- | ---- | ------------------------------------------------------------ |
+| memory | 单例 | 使用内存作为数据通道,不建议在生产环境使用 |
+| kafka | 集群 | 使用 Kafka 作为数据通道,使用全局事务ID作为分区策略,集群中的所有节点同时工作,可水平扩展,当配置了 spring.profiles.active=prd,cluster 参数后默认就使用 kafka 通道 |
+
+ 可以使用参数 `alpha.feature.akka.channel.type` 配置通道类型
+
+- Memory 通道参数
+
+| 参数名 | 参数值 | 说明 |
+| -------------------------------------- | ------ | ------------------------------------------- |
+| alpha.feature.akka.channel.type | memory | |
+| alpha.feature.akka.channel.memory.size | -1 | momory类型时内存队列大小,-1表示Integer.MAX |
+
+- Kafka 通道参数
+
+| 参数名 | 参数值 | 说明 |
+| --------------------------------------- | -------- | ------------------------------------------- |
+| alpha.feature.akka.channel.type | kafka | |
+| spring.kafka.bootstrap-servers | -1 | momory类型时内存队列大小,-1表示Integer.MAX |
+| spring.kafka.producer.batch-size | 16384 | |
+| spring.kafka.producer.retries | 0 | |
+| spring.kafka.producer.buffer.memory | 33554432 | |
+| spring.kafka.consumer.auto.offset.reset | earliest | |
+| spring.kafka.listener.pollTimeout | 1500 | |
+| kafka.numPartitions | 6 | |
+| kafka.replicationFactor | 1 | |
+
+
+
diff --git a/docs/fsm/fsm_manual_zh.md b/docs/fsm/fsm_manual.md
similarity index 56%
copy from docs/fsm/fsm_manual_zh.md
copy to docs/fsm/fsm_manual.md
index 7e81473..ab42442 100755
--- a/docs/fsm/fsm_manual_zh.md
+++ b/docs/fsm/fsm_manual.md
@@ -1,15 +1,15 @@
-# 状态机模式使用手册
+# 状态机模式
ServiceComb Pack 0.5.0 版本开始我们尝试使用状态机模型解决分布式事务中复杂的事件和状态关系,我们将 Alpha 看作一个可以记录每个全局事务不同状态的的盒子,Alpha 收到 Omega 发送的事务消息(全局事务启动、全局事务停止,全局事务失败,子事务启动,子事务停止,子事务失败等等)后完成一些动作(等待、补偿、超时)和状态切换。
-分布式事务的事件使我们面临很复杂的情况,我们希望可以通过一种DSL来清晰的定义状态机,并且能够解决状态机本身的持久化和分布式问题,再经过尝试后我们觉得 Akka FSM 是一个不错的选择。下面请跟我一起体验一下这个新功能。
+分布式事务的事件使我们面临很复杂的情况,我们希望可以通过一种DSL来清晰的定义状态机,并且能够解决状态机本身的持久化和分布式问题,再经过尝试后我们觉得 [Akka](https://github.com/akka/akka) 是一个不错的选择。下面请跟我一起体验一下这个新功能。
## 重大更新
* 使用 Akka 状态机代替基于表扫描的状态判断
* 性能提升一个数量级,事件吞吐量每秒1.8w+,全局事务处理量每秒1.2k+
* 内置健康指标采集器,可清晰了解系统瓶颈
-* 通过多种数据通道适配实现高可用
+* 支持分布式集群
* 向前兼容原有 gRPC 协议
* 全新的可视化监控界面
* 开放全新的 API
@@ -139,41 +139,109 @@ Sub Transactions 面板:本事务包含的子事务ID,子事务状态,子
## 集群
-可以通过部署多个 Alpha 实现处理能力的水平扩展和高可用,集群依赖 Kafka 服务。
-
-* 启动 Kafka,可以使用 docker compose 方式启动,以下是一个 compose 文件样例
-
- ```yaml
- version: '3.2'
- services:
- zookeeper:
- image: coolbeevip/alpine-zookeeper
- ports:
- - 2181:2181
- kafka:
- image: coolbeevip/alpine-kafka
- environment:
- KAFKA_ADVERTISED_HOST_NAME: 192.168.1.10
- KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
- ports:
- - 9092:9092
- links:
- - zookeeper
- ```
-
- **注意:** KAFKA_ADVERTISED_HOST_NAME 一定要配置成服务器的真实 IP 地址,不能配置成 127.0.0.1 或者 localhost
-
-* 启动两个 Alpha 节点
-
- 启动 Alpha 1
+> 需要下载主干代码后自己编译 0.6.0 版本
+
+依赖 Kafka 和 Redis 我们可以部署一个具有分布式处理能力的 Alpha 集群。Alpha 集群基于 Akka Cluster Sharding 和 Akka Persistence 实现动态计算和故障自愈。
+
+![image-20190927150455006](assets/alpha-cluster-architecture.png)
+
+上边是 Alpha 集群的工作架构图,表示部署了两个 Alpha 节点,分别是 8070 和 8071(这两编号是 [Gossip](https://en.wikipedia.org/wiki/Gossip_protocol) 协议的通信端口)。Omega 消息被发送到 Kafka ,并使用 globalTxId 作为分区策略,这保证了同一个全局事务下的子事务可以被有序的消费。KafkaConsumer 负责从 Kafak 中读取事件并发送给集群分片器 ShardingCoordinator,ShardingCoordinator 负责在 Alpha 集群中创建 SagaActor 并发送这个消息。运行中的 SagaActor 接收到消息后会持久化到 Redis 中,当这个集群中的节点奔溃后可以在集群其他节点恢复 SagaActor 以及它的状态。当 SagaActor 结束后就会将这一笔全局事务的数据存储到 ES。
+
+启动 Alpha 集群非常容易,首先启动集群需要用到的中间件 Kafka Redis PostgreSQL/MySQL ElasticSearch,你使用 Docker 启动他们(在生产环境建议使用一个更可靠的部署方式),下边提供了一个 docker compose 文件 servicecomb-pack-middleware.yml,你可以直接使用命令 `docker-compose -f servicecomb-pack-middleware.yml up -d` 启动它。
+
+```yaml
+version: '3.2'
+services:
+ postgres:
+ image: postgres:9.6
+ hostname: postgres
+ container_name: postgres
+ ports:
+ - '5432:5432'
+ environment:
+ - POSTGRES_DB=saga
+ - POSTGRES_USER=saga
+ - POSTGRES_PASSWORD=password
+
+ elasticsearch:
+ image: elasticsearch:6.6.2
+ hostname: elasticsearch
+ container_name: elasticsearch
+ environment:
+ - "ES_JAVA_OPTS=-Xmx256m -Xms256m"
+ - "discovery.type=single-node"
+ - "cluster.routing.allocation.disk.threshold_enabled=false"
+ ulimits:
+ memlock:
+ soft: -1
+ hard: -1
+ ports:
+ - 9200:9200
+ - 9300:9300
+
+ zookeeper:
+ image: coolbeevip/alpine-zookeeper:3.4.14
+ hostname: zookeeper
+ container_name: zookeeper
+ ports:
+ - 2181:2181
+
+ kafka:
+ image: coolbeevip/alpine-kafka:2.2.1-2.12
+ hostname: kafka
+ container_name: kafka
+ environment:
+ KAFKA_ADVERTISED_HOST_NAME: 10.50.8.3
+ KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
+ ports:
+ - 9092:9092
+ links:
+ - zookeeper:zookeeper
+ depends_on:
+ - zookeeper
+
+ redis:
+ image: redis:5.0.5-alpine
+ hostname: redis
+ container_name: redis
+ ports:
+ - 6379:6379
+```
+
+**注意:** KAFKA_ADVERTISED_HOST_NAME 一定要配置成服务器的真实 IP 地址,不能配置成 127.0.0.1 或者 localhost
+
+然后我们启动一个具有两个 Alpha 节点的集群,因为我是在一台机器上启动两个节点,所以他们必须具备不同的端口
+
+* 端口规划
+
+ | 节点 | gRPC 端口 | REST 端口 | Gossip 端口 |
+ | ------- | --------- | --------- | ----------- |
+ | Alpha 1 | 8080 | 8090 | 8070 |
+ | Alpha 2 | 8081 | 8091 | 8071 |
+
+* 集群参数
+
+ | 参数名 | 说明 |
+ | ------------------------------------------------ | ------------------------------------------------------------ |
+ | server.port | REST 端口,默认值 8090 |
+ | alpha.server.port | gRPC 端口,默认值 8080 |
+ | akkaConfig.akka.remote.artery.canonical.port | Gossip 端口,默认值 8070 |
+ | spring.kafka.bootstrap-server | Kafka 地址 |
+ | akkaConfig.akka-persistence-redis.redis.host | Redis Host IP |
+ | akkaConfig.akka-persistence-redis.redis.port | Redis Port |
+ | akkaConfig.akka-persistence-redis.redis.database | Redis Database |
+ | akkaConfig.akka.cluster.seed-nodes[N] | Gossip seed 节点地址,如果有多个 seed 节点,那么就写多行这个参数,每行的序号 N 从 0 开始采用递增方式 |
+ | spring.profiles.active | 必须填写 prd,cluster |
+
+* 启动 Alpha 1
```bash
- java -jar alpha-server-${version}-exec.jar \
+ java -jar alpha-server-0.6.0-SNAPSHOT-exec.jar \
--server.port=8090 \
--server.host=127.0.0.1 \
--alpha.server.port=8080 \
--alpha.feature.akka.enabled=true \
- --spring.datasource.url=jdbc:postgresql://0.0.0.0:5432/saga?useSSL=false \
+ --spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/saga?useSSL=false \
--spring.datasource.username=saga \
--spring.datasource.password=password \
--spring.kafka.bootstrap-servers=127.0.0.1:9092 \
@@ -186,15 +254,15 @@ Sub Transactions 面板:本事务包含的子事务ID,子事务状态,子
--spring.profiles.active=prd,cluster
```
- 启动 Alpha 2
+* 启动 Alpha 2
```bash
- java -jar alpha-server-${version}-exec.jar \
+ java -jar alpha-server-0.6.0-SNAPSHOT-exec.jar \
--server.port=8091 \
--server.host=127.0.0.1 \
--alpha.server.port=8081 \
--alpha.feature.akka.enabled=true \
- --spring.datasource.url=jdbc:postgresql://0.0.0.0:5432/saga?useSSL=false \
+ --spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/saga?useSSL=false \
--spring.datasource.username=saga \
--spring.datasource.password=password \
--spring.kafka.bootstrap-servers=127.0.0.1:9092 \
@@ -207,42 +275,20 @@ Sub Transactions 面板:本事务包含的子事务ID,子事务状态,子
--spring.profiles.active=prd,cluster
```
- 集群参数说明
-
- | 参数名 | 默认值 | 说明 |
- | ------------------------------------------------------------ | ------ | ------------------------------------------ |
- | server.port | 8090 | REST 端口,每个节点唯一 |
- | alpha.server.port | 8080 | GRPC 端口,每个节点唯一 |
- | spring.kafka.bootstrap-servers | | Kafka 访问地址 |
-| kafka.numPartitions | 6 | Kafka 访问地址 |
- | akkaConfig.akka.remote.artery.canonical.port | | Akka集群 端口,每个节点唯一 |
- | akkaConfig.akka.cluster.seed-nodes[x] | | Akka集群种子节点地址,每个种子节点配置一行 |
- | akkaConfig.akka-persistence-redis.redis.host | | Redis 服务地址 |
- | akkaConfig.akka-persistence-redis.redis.port | | Redis 服务端口 |
- | akkaConfig.akka-persistence-redis.redis.database | 0 | Redis 库名 |
- | alpha.feature.akka.transaction.repository.elasticsearch.batchSize | 100 | ES批量提交大小 |
- | alpha.feature.akka.transaction.repository.elasticsearch.refreshTime | 5000 | ES定时提交间隔 |
- | spring.profiles.active | | 激活配置,必须填写 prd,cluster |
-
-## 高可用
-
-集群部署时当一个节点宕机,那么另外一个节点会自动接管宕机节点未结束的 Actor
-
-**注意:** Alpha 采用"至少一次"的方式从 Kafka 接收事物事件,所以请确保 Kafka 服务的可靠性
-
-**注意:** Alpha 状态机采用 Redis 存储当前状态,并在节点宕机后通过 Redis 在集群其他节点上恢复状态机,所以请确保 Redis 服务的可靠性
+## 动态扩容
-**注意:** `alpha.feature.akka.transaction.repository.elasticsearch.batchSize` 设置的批量提交ES参数默认是100,在数据可靠性要求较高的场景请将此参数设置为 0
+- Alpha 支持通过动态增加节点的的方式实现在线处理能力扩容
+- Alpha 默认创建的 Kafka Topic 分区数量是 6,也就是说 Alpha 集群节点大于6个时将不能再提升处理性能,你可以根据规划在初次启动的时候使用 `kafka.numPartitions` 参数修改自动创建的 Topic 分区数
-## 动态扩容
+## 附件
-Alpha 收到事件消息后会放入 Kafka,Alpha 集群中的所有节点从 Kafka 中消费数据并发送给状态机处理,默认创建的 Topic 分区数量是 6,当你部署的集群节点数大于 6 时,你可以通过 `kafka.numPartitions` 参数修改默认分区数
+[事件通道](eventchannel_zh.md)
-## 后续计划
+[持久化](persistence_zh.md)
-APIs 集成 Swagger
+[Akka 配置](akka_zh.md)
-## 附件
+[APIs](apis_zh.md)
[设计文档](design_fsm_zh.md)
diff --git a/docs/fsm/fsm_manual_zh.md b/docs/fsm/fsm_manual_zh.md
index 7e81473..47db41e 100755
--- a/docs/fsm/fsm_manual_zh.md
+++ b/docs/fsm/fsm_manual_zh.md
@@ -1,15 +1,15 @@
-# 状态机模式使用手册
+# 状态机模式
ServiceComb Pack 0.5.0 版本开始我们尝试使用状态机模型解决分布式事务中复杂的事件和状态关系,我们将 Alpha 看作一个可以记录每个全局事务不同状态的的盒子,Alpha 收到 Omega 发送的事务消息(全局事务启动、全局事务停止,全局事务失败,子事务启动,子事务停止,子事务失败等等)后完成一些动作(等待、补偿、超时)和状态切换。
-分布式事务的事件使我们面临很复杂的情况,我们希望可以通过一种DSL来清晰的定义状态机,并且能够解决状态机本身的持久化和分布式问题,再经过尝试后我们觉得 Akka FSM 是一个不错的选择。下面请跟我一起体验一下这个新功能。
+分布式事务的事件使我们面临很复杂的情况,我们希望可以通过一种DSL来清晰的定义状态机,并且能够解决状态机本身的持久化和分布式问题,再经过尝试后我们觉得 [Akka](https://github.com/akka/akka) 是一个不错的选择。下面请跟我一起体验一下这个新功能。
## 重大更新
* 使用 Akka 状态机代替基于表扫描的状态判断
* 性能提升一个数量级,事件吞吐量每秒1.8w+,全局事务处理量每秒1.2k+
* 内置健康指标采集器,可清晰了解系统瓶颈
-* 通过多种数据通道适配实现高可用
+* 支持分布式集群
* 向前兼容原有 gRPC 协议
* 全新的可视化监控界面
* 开放全新的 API
@@ -139,41 +139,109 @@ Sub Transactions 面板:本事务包含的子事务ID,子事务状态,子
## 集群
-可以通过部署多个 Alpha 实现处理能力的水平扩展和高可用,集群依赖 Kafka 服务。
-
-* 启动 Kafka,可以使用 docker compose 方式启动,以下是一个 compose 文件样例
-
- ```yaml
- version: '3.2'
- services:
- zookeeper:
- image: coolbeevip/alpine-zookeeper
- ports:
- - 2181:2181
- kafka:
- image: coolbeevip/alpine-kafka
- environment:
- KAFKA_ADVERTISED_HOST_NAME: 192.168.1.10
- KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
- ports:
- - 9092:9092
- links:
- - zookeeper
- ```
-
- **注意:** KAFKA_ADVERTISED_HOST_NAME 一定要配置成服务器的真实 IP 地址,不能配置成 127.0.0.1 或者 localhost
-
-* 启动两个 Alpha 节点
-
- 启动 Alpha 1
+> 需要下载主干代码后自己编译 0.6.0 版本
+
+依赖 Kafka 和 Redis 我们可以部署一个具有分布式处理能力的 Alpha 集群。Alpha 集群基于 Akka Cluster Sharding 和 Akka Persistence 实现动态计算和故障自愈。
+
+![image-20190927150455006](assets/alpha-cluster-architecture.png)
+
+上边是 Alpha 集群的工作架构图,表示部署了两个 Alpha 节点,分别是 8070 和 8071(这两编号是 [Gossip](https://en.wikipedia.org/wiki/Gossip_protocol) 协议的通信端口)。Omega 消息被发送到 Kafka ,并使用 globalTxId 作为分区策略,这保证了同一个全局事务下的子事务可以被有序的消费。KafkaConsumer 负责从 Kafak 中读取事件并发送给集群分片器 ShardingCoordinator,ShardingCoordinator 负责在 Alpha 集群中创建 SagaActor 并发送这个消息。运行中的 SagaActor 接收到消息后会持久化到 Redis 中,当这个集群中的节点奔溃后可以在集群其他节点恢复 SagaActor 以及它的状态。当 SagaActor 结束后就会将这一笔全局事务的数据存储到 ES。
+
+启动 Alpha 集群非常容易,首先启动集群需要用到的中间件 Kafka Redis PostgreSQL/MySQL ElasticSearch,你使用 Docker 启动他们(在生产环境建议使用一个更可靠的部署方式),下边提供了一个 docker compose 文件 servicecomb-pack-middleware.yml,你可以直接使用命令 `docker-compose -f servicecomb-pack-middleware.yml up -d` 启动它。
+
+```yaml
+version: '3.2'
+services:
+ postgres:
+ image: postgres:9.6
+ hostname: postgres
+ container_name: postgres
+ ports:
+ - '5432:5432'
+ environment:
+ - POSTGRES_DB=saga
+ - POSTGRES_USER=saga
+ - POSTGRES_PASSWORD=password
+
+ elasticsearch:
+ image: elasticsearch:6.6.2
+ hostname: elasticsearch
+ container_name: elasticsearch
+ environment:
+ - "ES_JAVA_OPTS=-Xmx256m -Xms256m"
+ - "discovery.type=single-node"
+ - "cluster.routing.allocation.disk.threshold_enabled=false"
+ ulimits:
+ memlock:
+ soft: -1
+ hard: -1
+ ports:
+ - 9200:9200
+ - 9300:9300
+
+ zookeeper:
+ image: coolbeevip/alpine-zookeeper:3.4.14
+ hostname: zookeeper
+ container_name: zookeeper
+ ports:
+ - 2181:2181
+
+ kafka:
+ image: coolbeevip/alpine-kafka:2.2.1-2.12
+ hostname: kafka
+ container_name: kafka
+ environment:
+ KAFKA_ADVERTISED_HOST_NAME: 10.50.8.3
+ KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
+ ports:
+ - 9092:9092
+ links:
+ - zookeeper:zookeeper
+ depends_on:
+ - zookeeper
+
+ redis:
+ image: redis:5.0.5-alpine
+ hostname: redis
+ container_name: redis
+ ports:
+ - 6379:6379
+```
+
+**注意:** KAFKA_ADVERTISED_HOST_NAME 一定要配置成服务器的真实 IP 地址,不能配置成 127.0.0.1 或者 localhost
+
+然后我们启动一个具有两个 Alpha 节点的集群,因为我是在一台机器上启动两个节点,所以他们必须具备不同的端口
+
+* 端口规划
+
+ | 节点 | gRPC 端口 | REST 端口 | Gossip 端口 |
+ | ------- | --------- | --------- | ----------- |
+ | Alpha 1 | 8080 | 8090 | 8070 |
+ | Alpha 2 | 8081 | 8091 | 8071 |
+
+* 集群参数
+
+ | 参数名 | 说明 |
+ | ------------------------------------------------ | ------------------------------------------------------------ |
+ | server.port | REST 端口,默认值 8090 |
+ | alpha.server.port | gRPC 端口,默认值 8080 |
+ | akkaConfig.akka.remote.artery.canonical.port | Gossip 端口,默认值 8070 |
+ | spring.kafka.bootstrap-server | Kafka 地址 |
+ | akkaConfig.akka-persistence-redis.redis.host | Redis Host IP |
+ | akkaConfig.akka-persistence-redis.redis.port | Redis Port |
+ | akkaConfig.akka-persistence-redis.redis.database | Redis Database |
+ | akkaConfig.akka.cluster.seed-nodes[N] | Gossip seed 节点地址,如果有多个 seed 节点,那么就写多行这个参数,每行的序号 N 从 0 开始采用递增方式 |
+ | spring.profiles.active | 必须填写 prd,cluster |
+
+* 启动 Alpha 1
```bash
- java -jar alpha-server-${version}-exec.jar \
+ java -jar alpha-server-0.6.0-SNAPSHOT-exec.jar \
--server.port=8090 \
--server.host=127.0.0.1 \
--alpha.server.port=8080 \
--alpha.feature.akka.enabled=true \
- --spring.datasource.url=jdbc:postgresql://0.0.0.0:5432/saga?useSSL=false \
+ --spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/saga?useSSL=false \
--spring.datasource.username=saga \
--spring.datasource.password=password \
--spring.kafka.bootstrap-servers=127.0.0.1:9092 \
@@ -186,15 +254,15 @@ Sub Transactions 面板:本事务包含的子事务ID,子事务状态,子
--spring.profiles.active=prd,cluster
```
- 启动 Alpha 2
+* 启动 Alpha 2
```bash
- java -jar alpha-server-${version}-exec.jar \
+ java -jar alpha-server-0.6.0-SNAPSHOT-exec.jar \
--server.port=8091 \
--server.host=127.0.0.1 \
--alpha.server.port=8081 \
--alpha.feature.akka.enabled=true \
- --spring.datasource.url=jdbc:postgresql://0.0.0.0:5432/saga?useSSL=false \
+ --spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/saga?useSSL=false \
--spring.datasource.username=saga \
--spring.datasource.password=password \
--spring.kafka.bootstrap-servers=127.0.0.1:9092 \
@@ -207,42 +275,20 @@ Sub Transactions 面板:本事务包含的子事务ID,子事务状态,子
--spring.profiles.active=prd,cluster
```
- 集群参数说明
-
- | 参数名 | 默认值 | 说明 |
- | ------------------------------------------------------------ | ------ | ------------------------------------------ |
- | server.port | 8090 | REST 端口,每个节点唯一 |
- | alpha.server.port | 8080 | GRPC 端口,每个节点唯一 |
- | spring.kafka.bootstrap-servers | | Kafka 访问地址 |
-| kafka.numPartitions | 6 | Kafka 访问地址 |
- | akkaConfig.akka.remote.artery.canonical.port | | Akka集群 端口,每个节点唯一 |
- | akkaConfig.akka.cluster.seed-nodes[x] | | Akka集群种子节点地址,每个种子节点配置一行 |
- | akkaConfig.akka-persistence-redis.redis.host | | Redis 服务地址 |
- | akkaConfig.akka-persistence-redis.redis.port | | Redis 服务端口 |
- | akkaConfig.akka-persistence-redis.redis.database | 0 | Redis 库名 |
- | alpha.feature.akka.transaction.repository.elasticsearch.batchSize | 100 | ES批量提交大小 |
- | alpha.feature.akka.transaction.repository.elasticsearch.refreshTime | 5000 | ES定时提交间隔 |
- | spring.profiles.active | | 激活配置,必须填写 prd,cluster |
-
-## 高可用
-
-集群部署时当一个节点宕机,那么另外一个节点会自动接管宕机节点未结束的 Actor
-
-**注意:** Alpha 采用"至少一次"的方式从 Kafka 接收事物事件,所以请确保 Kafka 服务的可靠性
-
-**注意:** Alpha 状态机采用 Redis 存储当前状态,并在节点宕机后通过 Redis 在集群其他节点上恢复状态机,所以请确保 Redis 服务的可靠性
+## 动态扩容
-**注意:** `alpha.feature.akka.transaction.repository.elasticsearch.batchSize` 设置的批量提交ES参数默认是100,在数据可靠性要求较高的场景请将此参数设置为 0
+* Alpha 支持通过动态增加节点的的方式实现在线处理能力扩容
+* Alpha 默认创建的 Kafka Topic 分区数量是 6,也就是说 Alpha 集群节点大于6个时将不能再提升处理性能,你可以根据规划在初次启动的时候使用 `kafka.numPartitions` 参数修改自动创建的 Topic 分区数
-## 动态扩容
+## 附件
-Alpha 收到事件消息后会放入 Kafka,Alpha 集群中的所有节点从 Kafka 中消费数据并发送给状态机处理,默认创建的 Topic 分区数量是 6,当你部署的集群节点数大于 6 时,你可以通过 `kafka.numPartitions` 参数修改默认分区数
+[事件通道](eventchannel_zh.md)
-## 后续计划
+[事务数据持久化](persistence_zh.md)
-APIs 集成 Swagger
+[Akka 配置](akka_zh.md)
-## 附件
+[APIs](apis_zh.md)
[设计文档](design_fsm_zh.md)
diff --git a/docs/fsm/how_to_use_fsm.md b/docs/fsm/how_to_use_fsm.md
deleted file mode 100644
index a10643e..0000000
--- a/docs/fsm/how_to_use_fsm.md
+++ /dev/null
@@ -1,522 +0,0 @@
-# Alpha With State Machine
-
-## Quick Start
-
-The state machine mode save completed transaction data to elasticsearch
-
-* run postgress
-
- ```bash
- docker run -d -e "POSTGRES_DB=saga" -e "POSTGRES_USER=saga" -e "POSTGRES_PASSWORD=password" -p 5432:5432 postgres
- ```
-
-* run elasticsearch
-
- ```bash
- docker run --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:6.6.2
- ```
-
-* run Alpha
- use `alpha.feature.akka.enabled=true` enabled state machine mode support
-
- ```bash
- java -jar alpha-server-${version}-exec.jar \
- --spring.datasource.url=jdbc:postgresql://0.0.0.0:5432/saga?useSSL=false \
- --spring.datasource.username=saga \
- --spring.datasource.password=password \
- --spring.profiles.active=prd \
- --alpha.feature.akka.enabled=true \
- --alpha.feature.akka.transaction.repository.type=elasticsearch \
- --spring.data.elasticsearch.cluster-name=docker-cluster \
- --spring.data.elasticsearch.cluster-nodes=localhost:9300
- ```
-
- **NOTE:** `spring.data.elasticsearch.cluster-name` is elasticsearch cluster name, default is `docker-cluster` when run elasticsearch with docker, you can query cluster name by `curl http://localhost:9200/`
-
-* Omega
-
- use `alpha.feature.akka.enabled=true` enabled state machine mode support
-
- ```base
- alpha.feature.akka.enabled=true
- ```
-
-* WEB
-
- Open http://localhost:8090/admin in browser, [Screencast](https://youtu.be/ORoRkZeg8gA)
-
- Dashboard
-
- ![image-20190809122237766](assets/ui-dashboard.png)
-
- Transactions List
-
- ![image-20190809122324563](assets/ui-transactions-list.png)
-
- Transaction Details - Successful
-
- ![image-20190809122352852](assets/ui-transaction-details-successful.png)
-
- Transaction Details - Compensated
-
- ![image-20190809122516345](assets/ui-transaction-details-compensated.png)
-
- Transaction Details - Failed
-
- ![image-20190809122442186](assets/ui-transaction-details-failed.png)
-
-## APIs
-
-#### Metrics
-
-You can query Alpha metrics by RESTful API, Use the `AlphaBenchmark` to simulate sending data and quickly experience this feature.
-
-For exapmle; 10 concurrencies and send 1000 global transactions
-
-```bash
-java -jar alpha-benchmark-0.5.0-SNAPSHOT-exec.jar --alpha.cluster.address=0.0.0.0:8080 --w=0 --n=1000 --c=10
-```
-
-Query metrics
-
-```bash
-curl http://localhost:8090/alpha/api/v1/metrics
-
-{
- nodeType: "MASTER",
- metrics: {
- eventReceived: 8000,
- eventAccepted: 8000,
- eventRejected: 0,
- eventAvgTime: 0,
- actorReceived: 8000,
- actorAccepted: 8000,
- actorRejected: 0,
- actorAvgTime: 0,
- sagaBeginCounter: 1000,
- sagaEndCounter: 1000,
- sagaAvgTime: 9,
- committed: 1000,
- compensated: 0,
- suspended: 0,
- repositoryReceived: 1000,
- repositoryAccepted: 1000,
- repositoryRejected: 0,
- repositoryAvgTime: 0.88
- }
-}
-```
-
-description
-
-* eventReceived: number of gRPC events received
-* eventAccepted: number of gRPC events accepted(events into the channel)
-* eventRejected: number of gRPC events rejected
-* eventAvgTime: average elapsed time on events (milliseconds)
-* actorReceived: number of events received by actor
-* actorAccepted: number of events accepted by actor
-* actorRejected: number of events rejected by actor
-* actorAvgTime: average elapsed time on actor (milliseconds)
-* sagaBeginCounter: saga global transaction start counter
-* sagaEndCounter: saga global transaction end counter
-* sagaAvgTime: average elapsed time on saga global transaction (milliseconds)
-* committed: number of committed
-* compensated: number of compensated
-* suspended: number of suspended
-* repositoryReceived: number of events received by the repository component
-* repositoryAccepted: number of events accepted by the repository component
-* repositoryRejected: number of events rejected by the repository component
-* repositoryAvgTime: average elapsed time on save data (milliseconds)
-
-#### Query Transaction
-
-* Query all transactions
-
- ```bash
- curl -X GET http://localhost:8090/alpha/api/v1/transaction?page=0&size=50
-
- {
- "total": 2002,
- "page": 0,
- "size": 50,
- "elapsed": 581,
- "globalTransactions": [...]
- }
- ```
-
- Request
-
- * page
-
- * size
-
- Response
-
- * total
- * page
- * size
- * elapsed
- * globalTransactions
-
-* Query transaction by globalTxId
-
- ```bash
- curl -X GET http://localhost:8090/alpha/api/v1/transaction/{globalTxId}
-
- {
- "globalTxId": "e00a3bac-de6b-498f-99a4-c11d3087fd14",
- "type": "SAGA",
- "serviceName": "alpha-benchmark",
- "instanceId": "alpha-benchmark-127.0.0.1",
- "beginTime": 1564762932963,
- "endTime": 1564762933197,
- "state": "COMMITTED",
- "subTxSize": 3,
- "durationTime": 408,
- "subTransactions": [...],
- "events": [...]
- }
- ```
-
- Request
-
- * globalTxId: global transaction id
-
- Response
-
- * globalTxId: global transaction id
- * type: SAGA or TCC
- * serviceName: global transaction initiator service name
- * instanceId: global transaction initiator instance id
- * beginTime: global transaction start time
- * endTime: global transaction end time
- * state: global transaction final state (COMMITTED or COMPENSATED or SUSPENDED)
- * subTxSize: number of sub-transaction
- * durationTime
- * subTransactions: sub-transaction list
- * events: event list
-
-## Transactional Data Persistence
-
-Only the end of the transaction will be persisted to Elasticsearch, the transaction data in execution is persisted by Akka.
-
-The end transaction has the following
-
-* End of successfully: final state is COMMITTED
-
-* End of compensation: final state is COMPENSATED
-
-* End of abnormal: final state is SUSPENDED
-
- The following situations can lead to an abnormal end
-
- 1. timeout
- 2. Alpha received an unexpected event, For example, Alpha receive TxEndedEvent before TxStartedEvent or did not receive any sub-transaction events before receiving SagaEndedEvent
-
-### Persistent Configuration
-
-| name | default | description |
-| ------------------------------------------------------------ | ------- | ------------------------------------------------------------ |
-| alpha.feature.akka.transaction.repository.type | | Default is not persistent,currently only supports the elasticsearch option |
-| alpha.feature.akka.transaction.repository.elasticsearch.memory.size | -1 | Persistence wait queue length, default is Integer.MAX |
-| alpha.feature.akka.transaction.repository.elasticsearch.batchSize | 100 | Batch size |
-| alpha.feature.akka.transaction.repository.elasticsearch.refreshTime | 5000 | Refresh time |
-| spring.data.elasticsearch.cluster-name | | ES集群名称 |
-| spring.data.elasticsearch.cluster-nodes | | El;asticsearch address, For example, ip:9300 |
-
-### Elasticsearch Index Name
-Alpha will automatically create an index `alpha_global_transaction`
-
-### Query By Elasticsearch APIs
-
-* Query all transactions
-
- ```bash
- curl http://localhost:9200/alpha_global_transaction/_search
- ```
-
-* Query transaction by globalTxId
-
- ```bash
- curl -X POST http://localhost:9200/alpha_global_transaction/_search -H 'Content-Type: application/json' -d '
- {
- "query": {
- "bool": {
- "must": [{
- "term": {
- "globalTxId.keyword": "974d089a-5476-48ed-847a-1e338456809b"
- }
- }],
- "must_not": [],
- "should": []
- }
- },
- "from": 0,
- "size": 10,
- "sort": [],
- "aggs": {}
- }'
- ```
-
-* Result json data
-
- ```json
- {
- "took": 17,
- "timed_out": false,
- "_shards": {
- "total": 5,
- "successful": 5,
- "skipped": 0,
- "failed": 0
- },
- "hits": {
- "total": 4874,
- "max_score": 1.0,
- "hits": [{
- "_index": "alpha_global_transaction",
- "_type": "alpha_global_transaction_type",
- "_id": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "_score": 1.0,
- "_source": {
- "globalTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "type": "SAGA",
- "serviceName": "alpha-benchmark",
- "instanceId": "alpha-benchmark-127.0.0.1",
- "beginTime": 1563982631298,
- "endTime": 1563982631320,
- "state": "COMMITTED",
- "subTxSize": 3,
- "durationTime": 22,
- "subTransactions": [...],
- "events": [...]
- }
- },{...}]
- }
- }
- ```
-
-* Result data sample
-
- ```json
- {
- "took": 17,
- "timed_out": false,
- "_shards": {
- "total": 5,
- "successful": 5,
- "skipped": 0,
- "failed": 0
- },
- "hits": {
- "total": 4874,
- "max_score": 1.0,
- "hits": [{
- "_index": "alpha_global_transaction",
- "_type": "alpha_global_transaction_type",
- "_id": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "_score": 1.0,
- "_source": {
- "globalTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "type": "SAGA",
- "serviceName": "alpha-benchmark",
- "instanceId": "alpha-benchmark-127.0.0.1",
- "beginTime": 1563982631298,
- "endTime": 1563982631320,
- "state": "COMMITTED",
- "subTxSize": 3,
- "durationTime": 22,
- "subTransactions": [{
- "localTxId": "03fe15b2-a070-4e55-9b5b-801c2181dd0a",
- "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "beginTime": 1563982631308,
- "endTime": 1563982631309,
- "state": "COMMITTED",
- "durationTime": 1
- }, {
- "localTxId": "923f83fd-0bce-4fac-8c89-ecbe7c5e9106",
- "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "beginTime": 1563982631320,
- "endTime": 1563982631320,
- "state": "COMMITTED",
- "durationTime": 0
- }, {
- "localTxId": "95821ce3-2202-4e55-9343-4e6a6519821f",
- "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "beginTime": 1563982631309,
- "endTime": 1563982631309,
- "state": "COMMITTED",
- "durationTime": 0
- }],
- "events": [{
- "serviceName": "alpha-benchmark",
- "instanceId": "alpha-benchmark-127.0.0.1",
- "globalTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "localTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "createTime": 1563982631298,
- "timeout": 0,
- "type": "SagaStartedEvent"
- }, {
- "serviceName": "alpha-benchmark",
- "instanceId": "alpha-benchmark-127.0.0.1",
- "globalTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "localTxId": "03fe15b2-a070-4e55-9b5b-801c2181dd0a",
- "createTime": 1563982631299,
- "compensationMethod": "service a",
- "payloads": "AQE=",
- "retryMethod": "",
- "retries": 0,
- "type": "TxStartedEvent"
- }, {
- "serviceName": "alpha-benchmark",
- "instanceId": "alpha-benchmark-127.0.0.1",
- "globalTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "localTxId": "03fe15b2-a070-4e55-9b5b-801c2181dd0a",
- "createTime": 1563982631301,
- "type": "TxEndedEvent"
- }, {
- "serviceName": "alpha-benchmark",
- "instanceId": "alpha-benchmark-127.0.0.1",
- "globalTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "localTxId": "95821ce3-2202-4e55-9343-4e6a6519821f",
- "createTime": 1563982631302,
- "compensationMethod": "service b",
- "payloads": "AQE=",
- "retryMethod": "",
- "retries": 0,
- "type": "TxStartedEvent"
- }, {
- "serviceName": "alpha-benchmark",
- "instanceId": "alpha-benchmark-127.0.0.1",
- "globalTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "localTxId": "95821ce3-2202-4e55-9343-4e6a6519821f",
- "createTime": 1563982631304,
- "type": "TxEndedEvent"
- }, {
- "serviceName": "alpha-benchmark",
- "instanceId": "alpha-benchmark-127.0.0.1",
- "globalTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "localTxId": "923f83fd-0bce-4fac-8c89-ecbe7c5e9106",
- "createTime": 1563982631309,
- "compensationMethod": "service c",
- "payloads": "AQE=",
- "retryMethod": "",
- "retries": 0,
- "type": "TxStartedEvent"
- }, {
- "serviceName": "alpha-benchmark",
- "instanceId": "alpha-benchmark-127.0.0.1",
- "globalTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "localTxId": "923f83fd-0bce-4fac-8c89-ecbe7c5e9106",
- "createTime": 1563982631311,
- "type": "TxEndedEvent"
- }, {
- "serviceName": "alpha-benchmark",
- "instanceId": "alpha-benchmark-127.0.0.1",
- "globalTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "localTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "createTime": 1563982631312,
- "type": "SagaEndedEvent"
- }]
- }
- }]
- }
- }
- ```
-
- more references [Elasticsearch APIs](https://www.elastic.co/guide/en/elasticsearch/reference/6.6/docs.html)
-
-## High Availability
-
-You can achieve high availability of services by deploying an Alpha cluster. You can choose the type of event channel by parameter itself.
-
-### Event Channel Type
-
-Alpha receives the event sent by Omega and puts it into the event channel to wait for Akka processing.
-
-| Type | 模式 | description |
-| -------------------- | ------------ | ------------------------------------------------------------ |
-| memory(default) | single | Using memory as data channel, **Not recommended for use in production environments** |
-| redis(coming soon) | master-slave | Using redis PUB/SUB as data channel. Only the primary node is responsible for processing the data, After the master node is down, the slave node switches to the master node. |
-| kafka(coming soon) | cluster | Using Kafka as the data channel and global transaction ID as the partitioning strategy, support horizontally scalable. |
-
-* Memory channel
-
-| name | default | description |
-| -------------------------------------- | ------- | ---------------------------------- |
-| alpha.feature.akka.channel.type | memory | |
-| alpha.feature.akka.channel.memory.size | -1 | queue size, default is Integer.MAX |
-
-* Redis channel
-
- coming soon
-
-* Kafka channel
-
- coming soon
-
-### Akka Configuration
-
-Use the prefix `akkaConfig` before the parameter name of akka
-
-### Akka Persistence
-
-* Default
-
-```properties
-akkaConfig.akka.persistence.journal.plugin=akka.persistence.journal.inmem
-akkaConfig.akka.persistence.journal.leveldb.dir=target/example/journal
-akkaConfig.akka.persistence.snapshot-store.plugin=akka.persistence.snapshot-store.local
-akkaConfig.akka.persistence.snapshot-store.local.dir=target/example/snapshots
-```
-
-* Redis
-
-```properties
-akkaConfig.akka.persistence.journal.plugin=akka-persistence-redis.journal
-akkaConfig.akka.persistence.snapshot-store.plugin=akka-persistence-redis.snapshot
-akkaConfig.akka-persistence-redis.redis.mode=simple
-akkaConfig.akka-persistence-redis.redis.host=localhost
-akkaConfig.akka-persistence-redis.redis.port=6379
-akkaConfig.akka-persistence-redis.redis.database=0
-```
-
-more references [akka-persistence-redis](https://index.scala-lang.org/safety-data/akka-persistence-redis/akka-persistence-redis/0.4.0?target=_2.11)
-
-Usage example
-
-```bash
-java -jar alpha-server-${version}-exec.jar \
- --spring.datasource.url=jdbc:postgresql://0.0.0.0:5432/saga?useSSL=false \
- --spring.datasource.username=saga-user \
- --spring.datasource.password=saga-password \
- --spring.profiles.active=prd \
- --alpha.feature.akka.enabled=true \
- --alpha.feature.akka.transaction.repository.type=elasticsearch \
- --spring.data.elasticsearch.cluster-name=docker-cluster \
- --spring.data.elasticsearch.cluster-nodes=localhost:9300 \
- --akkaConfig.akka.persistence.journal.plugin=akka-persistence-redis.journal \
- --akkaConfig.akka.persistence.snapshot-store.plugin=akka-persistence-redis.snapshot \
- --akkaConfig.akka-persistence-redis.redis.mode=simple \
- --akkaConfig.akka-persistence-redis.redis.host=localhost \
- --akkaConfig.akka-persistence-redis.redis.port=6379 \
- --akkaConfig.akka-persistence-redis.redis.database=0
-```
-
-### Akka Cluster
-
-coming soon
-
-## Appendix
-
-[design document](design_fsm_zh.md)
-
-[benchmark](benchmark_zh.md)
\ No newline at end of file
diff --git a/docs/fsm/how_to_use_fsm_zh.md b/docs/fsm/how_to_use_fsm_zh.md
deleted file mode 100644
index cc0111e..0000000
--- a/docs/fsm/how_to_use_fsm_zh.md
+++ /dev/null
@@ -1,527 +0,0 @@
-# Alpha 状态机模式
-
-## 快速开始
-
-状态机模式使用 Elasticsearch 存储已结束的事务数据
-
-* 启动 Postgress
-
- ```bash
- docker run -d -e "POSTGRES_DB=saga" -e "POSTGRES_USER=saga" -e "POSTGRES_PASSWORD=password" -p 5432:5432 postgres
- ```
-
-* 启动 Elasticsearch
-
- ```bash
- docker run --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:6.6.2
- ```
-
-* 启动 Alpha
- 使用 `alpha.feature.akka.enabled=true` 开启状态机模式
-
- ```bash
- java -jar alpha-server-${version}-exec.jar \
- --spring.datasource.url=jdbc:postgresql://0.0.0.0:5432/saga?useSSL=false \
- --spring.datasource.username=saga \
- --spring.datasource.password=password \
- --spring.profiles.active=prd \
- --alpha.feature.akka.enabled=true \
- --alpha.feature.akka.transaction.repository.type=elasticsearch \
- --spring.data.elasticsearch.cluster-name=docker-cluster \
- --spring.data.elasticsearch.cluster-nodes=localhost:9300
- ```
-
- 更多持久化参数参见 "事务数据持久化" 说明
-
- **注意:** 参数 `spring.data.elasticsearch.cluster-name` 设置的是 Elasticsearch 集群名称,使用 docker 启动 Elasticsearch 默认集群名称是 `docker-cluster` , 你可以使用 `curl http://localhost:9200/` 命令查询
-
-* Omega 侧配置
-
- 使用 `alpha.feature.akka.enabled=true` 开启状态机模式
-
- ```base
- alpha.feature.akka.enabled=true
- ```
-
-* WEB管理界面
-
- 在浏览器中打开 http://localhost:8090/admin,[视频截屏](https://youtu.be/ORoRkZeg8gA)
-
- 仪表盘
-
- ![image-20190809122237766](assets/ui-dashboard.png)
-
- 事务列表
-
- ![image-20190809122324563](assets/ui-transactions-list.png)
-
- 事务明细-成功
-
- ![image-20190809122352852](assets/ui-transaction-details-successful.png)
-
- 事务明细-补偿
-
- ![image-20190809122516345](assets/ui-transaction-details-compensated.png)
-
- 事务明细-失败
-
- ![image-20190809122442186](assets/ui-transaction-details-failed.png)
-
-## APIs
-
-#### 性能度量
-
-你可以使用 API 查询 Alpha 的性能指标,你可以使用基准测试工具 `AlphaBenchmark` 模拟发送数据后快速体验这一功能
-
-例如:使用以下命令模拟 10 并发,发送 1000 个全局事务
-
-```bash
-java -jar alpha-benchmark-0.5.0-SNAPSHOT-exec.jar --alpha.cluster.address=0.0.0.0:8080 --w=0 --n=1000 --c=10
-```
-
-查询性能指标
-
-```bash
-curl http://localhost:8090/alpha/api/v1/metrics
-
-{
- nodeType: "MASTER",
- metrics: {
- eventReceived: 8000,
- eventAccepted: 8000,
- eventRejected: 0,
- eventAvgTime: 0,
- actorReceived: 8000,
- actorAccepted: 8000,
- actorRejected: 0,
- actorAvgTime: 0,
- sagaBeginCounter: 1000,
- sagaEndCounter: 1000,
- sagaAvgTime: 9,
- committed: 1000,
- compensated: 0,
- suspended: 0,
- repositoryReceived: 1000,
- repositoryAccepted: 1000,
- repositoryRejected: 0,
- repositoryAvgTime: 0.88
- }
-}
-```
-
-例如以上指标中显示 `sagaAvgTime: 9` 表示每个全局事务在Akka的处理耗时9毫秒,`repositoryAvgTime: 0.88` 表示每个全局事务入库耗时0.88毫秒
-
-指标说明
-
-* eventReceived: Alpha 收到的 gRPC 事件数量
-* eventAccepted: Alpha 处理的 gRPC 事件数量(事件放入事件通道)
-* eventRejected: Alpha 拒绝的 gRPC 事件数量
-* eventAvgTime: Alpha 平均耗时(毫秒)
-* actorReceived: Akka 收到的事件数量
-* actorAccepted: Akka 处理的事件数量
-* actorRejected: Akka 拒绝的事件数量
-* actorAvgTime: Akka 平均耗时(毫秒)
-* sagaBeginCounter: 开始的 Saga 全局事务数量
-* sagaEndCounter: 结束的 Saga 全局事务数量
-* sagaAvgTime: 平均耗时(毫秒)
-* committed: COMMITTED状态的 Saga 全局事务数量
-* compensated: COMPENSATED状态的 Saga 全局事务数量
-* suspended: SUSPENDED状态的 Saga 的全局事务数量
-* repositoryReceived: 存储模块收到的全局事务数量
-* repositoryAccepted: 存储模块处理的全局事务数量
-* repositoryRejected: 存储模块拒绝的全局事务数量
-* repositoryAvgTime: 平均耗时(毫秒)
-
-#### 事务数据查询
-
-> 需要启动 Elasticsearch 存储事务
-
-* 查询事务列表
-
- ```bash
- curl -X GET http://localhost:8090/alpha/api/v1/transaction?page=0&size=50
-
- {
- "total": 2002,
- "page": 0,
- "size": 50,
- "elapsed": 581,
- "globalTransactions": [...]
- }
- ```
-
- 请求参数
-
- * page 页号
-
- * size 返回行数
-
- 返回参数
-
- * total 总行数
- * page 本次查询结果页号
- * size 本次查询行数
- * elapsed 本次查询耗时(毫秒)
- * globalTransactions 事件数据列表
-
-* 查询一条事务
-
- ```bash
- curl -X GET http://localhost:8090/alpha/api/v1/transaction/{globalTxId}
-
- {
- "globalTxId": "e00a3bac-de6b-498f-99a4-c11d3087fd14",
- "type": "SAGA",
- "serviceName": "alpha-benchmark",
- "instanceId": "alpha-benchmark-127.0.0.1",
- "beginTime": 1564762932963,
- "endTime": 1564762933197,
- "state": "COMMITTED",
- "subTxSize": 3,
- "durationTime": 408,
- "subTransactions": [...],
- "events": [...]
- }
- ```
-
- 请求参数
-
- * globalTxId 全局事务ID
-
- 返回参数
-
- * globalTxId 全局事务ID
- * type 事务类型,目前只有SAGA,后期增加TCC
- * serviceName 全局事务发起方服务名称
- * instanceId 全局事务发起方实例ID
- * beginTime 事务开始时间
- * endTime 事务结束时间
- * state 事务最终状态
- * subTxSize 包含子事务数量
- * durationTime 全局事务处理耗时
- * subTransactions 子事务数据列表
- * events 事件列表
-
-## 事务数据持久化
-
-只有结束的事务才会被持久化到 Elasticsearch,执行中的事务数据通过Akka持久化。事务结束状态有以下几种
-
-* 事务成功结束,最后状态为 COMMITTED
-
-* 事务补偿后结束,最后状态为 COMPENSATED
-
-* 事务异常结束,最后状态为 SUSPENDED
-
- 导致事务异常结束有以下几种情况
-
- 1. 事务超时
- 2. Alpha收到了不符合预期的事件,例如在 收到 TxStartedEvent 前就收到了 TxEndedEvent,或者没有收到任何子事务事件就收到了 SagaEndedEvent等,这些规则都被定义在了有限状态机中。
-
-### 持久化参数
-
-| 参数名 | 默认值 | 说明 |
-| ------------------------------------------------------------ | ------ | ------------------------------------------------------------ |
-| alpha.feature.akka.transaction.repository.type | | 持久化类型,目前可选值 elasticsearch,如果不设置则不存储 |
-| alpha.feature.akka.transaction.repository.elasticsearch.memory.size | -1 | 持久化数据队列,默认 Integer.MAX. Actor会将终止的事务数据放入此队列,并等待存入elasticsearch |
-| alpha.feature.akka.transaction.repository.elasticsearch.batchSize | 100 | elasticsearch 批量入库数量 |
-| alpha.feature.akka.transaction.repository.elasticsearch.refreshTime | 5000 | elasticsearch 定时同步到ES时间 |
-| spring.data.elasticsearch.cluster-name | | ES集群名称 |
-| spring.data.elasticsearch.cluster-nodes | | ES节点地址,格式:localhost:9300,多个地址逗号分隔 |
-
-### Elasticsearch 索引
-Alpha 会在 Elasticsearch 中创建一个名为 `alpha_global_transaction` 的索引
-
-### 使用 Elasticsearch APIs 查询事务数据
-
-* 查询所有事务
-
- ```bash
- curl http://localhost:9200/alpha_global_transaction/_search
- ```
-
-* 查询匹配 globalTxId 的事务
-
- ```bash
- curl -X POST http://localhost:9200/alpha_global_transaction/_search -H 'Content-Type: application/json' -d '
- {
- "query": {
- "bool": {
- "must": [{
- "term": {
- "globalTxId.keyword": "974d089a-5476-48ed-847a-1e338456809b"
- }
- }],
- "must_not": [],
- "should": []
- }
- },
- "from": 0,
- "size": 10,
- "sort": [],
- "aggs": {}
- }'
- ```
-
-* 查询返回 JSON 格式
-
- ```json
- {
- "took": 17,
- "timed_out": false,
- "_shards": {
- "total": 5,
- "successful": 5,
- "skipped": 0,
- "failed": 0
- },
- "hits": {
- "total": 4874,
- "max_score": 1.0,
- "hits": [{
- "_index": "alpha_global_transaction",
- "_type": "alpha_global_transaction_type",
- "_id": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "_score": 1.0,
- "_source": {
- "globalTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "type": "SAGA",
- "serviceName": "alpha-benchmark",
- "instanceId": "alpha-benchmark-127.0.0.1",
- "beginTime": 1563982631298,
- "endTime": 1563982631320,
- "state": "COMMITTED",
- "subTxSize": 3,
- "durationTime": 22,
- "subTransactions": [...],
- "events": [...]
- }
- },{...}]
- }
- }
- ```
-
-* 查询返回 JSON样例
-
- ```json
- {
- "took": 17,
- "timed_out": false,
- "_shards": {
- "total": 5,
- "successful": 5,
- "skipped": 0,
- "failed": 0
- },
- "hits": {
- "total": 4874,
- "max_score": 1.0,
- "hits": [{
- "_index": "alpha_global_transaction",
- "_type": "alpha_global_transaction_type",
- "_id": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "_score": 1.0,
- "_source": {
- "globalTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "type": "SAGA",
- "serviceName": "alpha-benchmark",
- "instanceId": "alpha-benchmark-127.0.0.1",
- "beginTime": 1563982631298,
- "endTime": 1563982631320,
- "state": "COMMITTED",
- "subTxSize": 3,
- "durationTime": 22,
- "subTransactions": [{
- "localTxId": "03fe15b2-a070-4e55-9b5b-801c2181dd0a",
- "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "beginTime": 1563982631308,
- "endTime": 1563982631309,
- "state": "COMMITTED",
- "durationTime": 1
- }, {
- "localTxId": "923f83fd-0bce-4fac-8c89-ecbe7c5e9106",
- "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "beginTime": 1563982631320,
- "endTime": 1563982631320,
- "state": "COMMITTED",
- "durationTime": 0
- }, {
- "localTxId": "95821ce3-2202-4e55-9343-4e6a6519821f",
- "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "beginTime": 1563982631309,
- "endTime": 1563982631309,
- "state": "COMMITTED",
- "durationTime": 0
- }],
- "events": [{
- "serviceName": "alpha-benchmark",
- "instanceId": "alpha-benchmark-127.0.0.1",
- "globalTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "localTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "createTime": 1563982631298,
- "timeout": 0,
- "type": "SagaStartedEvent"
- }, {
- "serviceName": "alpha-benchmark",
- "instanceId": "alpha-benchmark-127.0.0.1",
- "globalTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "localTxId": "03fe15b2-a070-4e55-9b5b-801c2181dd0a",
- "createTime": 1563982631299,
- "compensationMethod": "service a",
- "payloads": "AQE=",
- "retryMethod": "",
- "retries": 0,
- "type": "TxStartedEvent"
- }, {
- "serviceName": "alpha-benchmark",
- "instanceId": "alpha-benchmark-127.0.0.1",
- "globalTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "localTxId": "03fe15b2-a070-4e55-9b5b-801c2181dd0a",
- "createTime": 1563982631301,
- "type": "TxEndedEvent"
- }, {
- "serviceName": "alpha-benchmark",
- "instanceId": "alpha-benchmark-127.0.0.1",
- "globalTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "localTxId": "95821ce3-2202-4e55-9343-4e6a6519821f",
- "createTime": 1563982631302,
- "compensationMethod": "service b",
- "payloads": "AQE=",
- "retryMethod": "",
- "retries": 0,
- "type": "TxStartedEvent"
- }, {
- "serviceName": "alpha-benchmark",
- "instanceId": "alpha-benchmark-127.0.0.1",
- "globalTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "localTxId": "95821ce3-2202-4e55-9343-4e6a6519821f",
- "createTime": 1563982631304,
- "type": "TxEndedEvent"
- }, {
- "serviceName": "alpha-benchmark",
- "instanceId": "alpha-benchmark-127.0.0.1",
- "globalTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "localTxId": "923f83fd-0bce-4fac-8c89-ecbe7c5e9106",
- "createTime": 1563982631309,
- "compensationMethod": "service c",
- "payloads": "AQE=",
- "retryMethod": "",
- "retries": 0,
- "type": "TxStartedEvent"
- }, {
- "serviceName": "alpha-benchmark",
- "instanceId": "alpha-benchmark-127.0.0.1",
- "globalTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "localTxId": "923f83fd-0bce-4fac-8c89-ecbe7c5e9106",
- "createTime": 1563982631311,
- "type": "TxEndedEvent"
- }, {
- "serviceName": "alpha-benchmark",
- "instanceId": "alpha-benchmark-127.0.0.1",
- "globalTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "localTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
- "createTime": 1563982631312,
- "type": "SagaEndedEvent"
- }]
- }
- }]
- }
- }
- ```
-
- 更多用法参考 [Elasticsearch APIs](https://www.elastic.co/guide/en/elasticsearch/reference/6.6/docs.html)
-
-## 高可用
-
-可以通过部署 Alpha 集群实现服务的高可用,你可以通过参数自己选择事件通道的类型
-
-### 事件通道类型
-
-Alpha 收到 Omeag 发送的事件后放入事件通道等待Akka处理
-
-| 通道类型 | 模式 | 说明 |
-| -------------------- | ---- | ------------------------------------------------------------ |
-| memory(默认) | 单例 | 使用内存作为数据通道,不建议在生产环境使用 |
-| redis(coming soon) | 主从 | 使用 Redis PUB/SUB 作为数据通道,集群中的主节点负责处理数据,从节点处于就绪状态,主节点宕机后从节点接管主节点 |
-| kafka(coming soon) | 集群 | 使用 Kafka 作为数据通道,使用全局事务ID作为分区策略,集群中的所有节点同时工作,可水平扩展 |
-
-
- 可以使用参数 `alpha.feature.akka.channel.type` 配置通道类型
-
-* Memory channel
-
-| 参数名 | 默认值 | 说明 |
-| -------------------------------------- | ------ | ------------------------------------------- |
-| alpha.feature.akka.channel.type | memory | 可选类型有 activemq, kafka, redis |
-| alpha.feature.akka.channel.memory.size | -1 | momory类型时内存队列大小,-1表示Integer.MAX |
-
-* Redis channel
-
- coming soon
-
-* Kafka channel
-
- coming soon
-
-### Akka 参数配置
-
-可以通过 `akkaConfig.{akka_key} = value` 方式配置Akka参数,例如系统默认的基于内存模式的配置
-
-### Akka 持久化
-
-```properties
-akkaConfig.akka.persistence.journal.plugin=akka.persistence.journal.inmem
-akkaConfig.akka.persistence.journal.leveldb.dir=target/example/journal
-akkaConfig.akka.persistence.snapshot-store.plugin=akka.persistence.snapshot-store.local
-akkaConfig.akka.persistence.snapshot-store.local.dir=target/example/snapshots
-```
-
-你可以通过参数配置成基于 Redis 的持久化方式
-
-```properties
-akkaConfig.akka.persistence.journal.plugin=akka-persistence-redis.journal
-akkaConfig.akka.persistence.snapshot-store.plugin=akka-persistence-redis.snapshot
-akkaConfig.akka-persistence-redis.redis.mode=simple
-akkaConfig.akka-persistence-redis.redis.host=localhost
-akkaConfig.akka-persistence-redis.redis.port=6379
-akkaConfig.akka-persistence-redis.redis.database=0
-```
-
-更多参数请参考 [akka-persistence-redis](https://index.scala-lang.org/safety-data/akka-persistence-redis/akka-persistence-redis/0.4.0?target=_2.11)
-
-你可以在 Alpha 的启动命令中直接设置这些参数,例如
-
-```bash
-java -jar alpha-server-${version}-exec.jar \
- --spring.datasource.url=jdbc:postgresql://0.0.0.0:5432/saga?useSSL=false \
- --spring.datasource.username=saga-user \
- --spring.datasource.password=saga-password \
- --spring.profiles.active=prd \
- --alpha.feature.akka.enabled=true \
- --alpha.feature.akka.transaction.repository.type=elasticsearch \
- --spring.data.elasticsearch.cluster-name=docker-cluster \
- --spring.data.elasticsearch.cluster-nodes=localhost:9300 \
- --akkaConfig.akka.persistence.journal.plugin=akka-persistence-redis.journal \
- --akkaConfig.akka.persistence.snapshot-store.plugin=akka-persistence-redis.snapshot \
- --akkaConfig.akka-persistence-redis.redis.mode=simple \
- --akkaConfig.akka-persistence-redis.redis.host=localhost \
- --akkaConfig.akka-persistence-redis.redis.port=6379 \
- --akkaConfig.akka-persistence-redis.redis.database=0
-```
-
-### Akka 集群
-
-coming soon
-
-## 附录
-
-[设计文档](design_fsm_zh.md)
-
-[基准测试报告](benchmark_zh.md)
\ No newline at end of file
diff --git a/docs/fsm/persistence_zh.md b/docs/fsm/persistence_zh.md
new file mode 100644
index 0000000..9a0065a
--- /dev/null
+++ b/docs/fsm/persistence_zh.md
@@ -0,0 +1,235 @@
+# 事务数据持久化
+
+只有结束的事务才会被持久化到 Elasticsearch,执行中的事务数据通过Akka持久化。事务结束状态有以下几种
+
+- 事务成功结束,最后状态为 COMMITTED
+
+- 事务补偿后结束,最后状态为 COMPENSATED
+
+- 事务异常结束,最后状态为 SUSPENDED
+
+ 导致事务异常结束有以下几种情况
+
+ 1. 事务超时
+ 2. Alpha收到了不符合预期的事件,例如在 收到 TxStartedEvent 前就收到了 TxEndedEvent,或者没有收到任何子事务事件就收到了 SagaEndedEvent等,这些规则都被定义在了有限状态机中。
+
+### 持久化参数
+
+| 参数名 | 默认值 | 说明 |
+| ------------------------------------------------------------ | ------ | ------------------------------------------------------------ |
+| alpha.feature.akka.transaction.repository.type | | 持久化类型,目前可选值 elasticsearch,如果不设置则不存储 |
+| alpha.feature.akka.transaction.repository.elasticsearch.batchSize | 100 | elasticsearch 批量入库数量 |
+| alpha.feature.akka.transaction.repository.elasticsearch.refreshTime | 5000 | elasticsearch 定时同步到ES时间 |
+| spring.data.elasticsearch.cluster-name | | ES集群名称 |
+| spring.data.elasticsearch.cluster-nodes | | ES节点地址,格式:localhost:9300,多个地址逗号分隔 |
+
+### Elasticsearch 索引
+
+Alpha 会在 Elasticsearch 中创建一个名为 `alpha_global_transaction` 的索引
+
+### 使用 Elasticsearch APIs 查询事务数据
+
+- 查询所有事务
+
+ ```bash
+ curl http://localhost:9200/alpha_global_transaction/_search
+ ```
+
+- 查询匹配 globalTxId 的事务
+
+ ```bash
+ curl -X POST http://localhost:9200/alpha_global_transaction/_search -H 'Content-Type: application/json' -d '
+ {
+ "query": {
+ "bool": {
+ "must": [{
+ "term": {
+ "globalTxId.keyword": "974d089a-5476-48ed-847a-1e338456809b"
+ }
+ }],
+ "must_not": [],
+ "should": []
+ }
+ },
+ "from": 0,
+ "size": 10,
+ "sort": [],
+ "aggs": {}
+ }'
+ ```
+
+- 查询返回 JSON 格式
+
+ ```json
+ {
+ "took": 17,
+ "timed_out": false,
+ "_shards": {
+ "total": 5,
+ "successful": 5,
+ "skipped": 0,
+ "failed": 0
+ },
+ "hits": {
+ "total": 4874,
+ "max_score": 1.0,
+ "hits": [{
+ "_index": "alpha_global_transaction",
+ "_type": "alpha_global_transaction_type",
+ "_id": "209791a0-34f4-40da-807e-9c5b8786dd61",
+ "_score": 1.0,
+ "_source": {
+ "globalTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
+ "type": "SAGA",
+ "serviceName": "alpha-benchmark",
+ "instanceId": "alpha-benchmark-127.0.0.1",
+ "beginTime": 1563982631298,
+ "endTime": 1563982631320,
+ "state": "COMMITTED",
+ "subTxSize": 3,
+ "durationTime": 22,
+ "subTransactions": [...],
+ "events": [...]
+ }
+ },{...}]
+ }
+ }
+ ```
+
+- 查询返回 JSON样例
+
+ ```json
+ {
+ "took": 17,
+ "timed_out": false,
+ "_shards": {
+ "total": 5,
+ "successful": 5,
+ "skipped": 0,
+ "failed": 0
+ },
+ "hits": {
+ "total": 4874,
+ "max_score": 1.0,
+ "hits": [{
+ "_index": "alpha_global_transaction",
+ "_type": "alpha_global_transaction_type",
+ "_id": "209791a0-34f4-40da-807e-9c5b8786dd61",
+ "_score": 1.0,
+ "_source": {
+ "globalTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
+ "type": "SAGA",
+ "serviceName": "alpha-benchmark",
+ "instanceId": "alpha-benchmark-127.0.0.1",
+ "beginTime": 1563982631298,
+ "endTime": 1563982631320,
+ "state": "COMMITTED",
+ "subTxSize": 3,
+ "durationTime": 22,
+ "subTransactions": [{
+ "localTxId": "03fe15b2-a070-4e55-9b5b-801c2181dd0a",
+ "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
+ "beginTime": 1563982631308,
+ "endTime": 1563982631309,
+ "state": "COMMITTED",
+ "durationTime": 1
+ }, {
+ "localTxId": "923f83fd-0bce-4fac-8c89-ecbe7c5e9106",
+ "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
+ "beginTime": 1563982631320,
+ "endTime": 1563982631320,
+ "state": "COMMITTED",
+ "durationTime": 0
+ }, {
+ "localTxId": "95821ce3-2202-4e55-9343-4e6a6519821f",
+ "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
+ "beginTime": 1563982631309,
+ "endTime": 1563982631309,
+ "state": "COMMITTED",
+ "durationTime": 0
+ }],
+ "events": [{
+ "serviceName": "alpha-benchmark",
+ "instanceId": "alpha-benchmark-127.0.0.1",
+ "globalTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
+ "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
+ "localTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
+ "createTime": 1563982631298,
+ "timeout": 0,
+ "type": "SagaStartedEvent"
+ }, {
+ "serviceName": "alpha-benchmark",
+ "instanceId": "alpha-benchmark-127.0.0.1",
+ "globalTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
+ "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
+ "localTxId": "03fe15b2-a070-4e55-9b5b-801c2181dd0a",
+ "createTime": 1563982631299,
+ "compensationMethod": "service a",
+ "payloads": "AQE=",
+ "retryMethod": "",
+ "retries": 0,
+ "type": "TxStartedEvent"
+ }, {
+ "serviceName": "alpha-benchmark",
+ "instanceId": "alpha-benchmark-127.0.0.1",
+ "globalTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
+ "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
+ "localTxId": "03fe15b2-a070-4e55-9b5b-801c2181dd0a",
+ "createTime": 1563982631301,
+ "type": "TxEndedEvent"
+ }, {
+ "serviceName": "alpha-benchmark",
+ "instanceId": "alpha-benchmark-127.0.0.1",
+ "globalTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
+ "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
+ "localTxId": "95821ce3-2202-4e55-9343-4e6a6519821f",
+ "createTime": 1563982631302,
+ "compensationMethod": "service b",
+ "payloads": "AQE=",
+ "retryMethod": "",
+ "retries": 0,
+ "type": "TxStartedEvent"
+ }, {
+ "serviceName": "alpha-benchmark",
+ "instanceId": "alpha-benchmark-127.0.0.1",
+ "globalTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
+ "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
+ "localTxId": "95821ce3-2202-4e55-9343-4e6a6519821f",
+ "createTime": 1563982631304,
+ "type": "TxEndedEvent"
+ }, {
+ "serviceName": "alpha-benchmark",
+ "instanceId": "alpha-benchmark-127.0.0.1",
+ "globalTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
+ "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
+ "localTxId": "923f83fd-0bce-4fac-8c89-ecbe7c5e9106",
+ "createTime": 1563982631309,
+ "compensationMethod": "service c",
+ "payloads": "AQE=",
+ "retryMethod": "",
+ "retries": 0,
+ "type": "TxStartedEvent"
+ }, {
+ "serviceName": "alpha-benchmark",
+ "instanceId": "alpha-benchmark-127.0.0.1",
+ "globalTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
+ "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
+ "localTxId": "923f83fd-0bce-4fac-8c89-ecbe7c5e9106",
+ "createTime": 1563982631311,
+ "type": "TxEndedEvent"
+ }, {
+ "serviceName": "alpha-benchmark",
+ "instanceId": "alpha-benchmark-127.0.0.1",
+ "globalTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
+ "parentTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
+ "localTxId": "209791a0-34f4-40da-807e-9c5b8786dd61",
+ "createTime": 1563982631312,
+ "type": "SagaEndedEvent"
+ }]
+ }
+ }]
+ }
+ }
+ ```
+
+ 更多用法参考 [Elasticsearch APIs](https://www.elastic.co/guide/en/elasticsearch/reference/6.6/docs.html)
\ No newline at end of file
diff --git a/docs/user_guide.md b/docs/user_guide.md
index 34150f0..c927bb5 100644
--- a/docs/user_guide.md
+++ b/docs/user_guide.md
@@ -816,4 +816,4 @@ Alpha can be highly available by deploying multiple instances, enable cluster su
## Experiment
-[Alpha State Machine Mode](fsm/how_to_use_fsm.md)
\ No newline at end of file
+[State Machine Mode](fsm/fsm_manual.md)
\ No newline at end of file
diff --git a/docs/user_guide_zh.md b/docs/user_guide_zh.md
index 91ccf52..78f468b 100644
--- a/docs/user_guide_zh.md
+++ b/docs/user_guide_zh.md
@@ -802,4 +802,4 @@ Alpha 可以通过部署多实例的方式保证高可用,使用 `alpha.cluste
## 实验
-[Alpha 状态机模式](fsm/fsm_manual_zh.md)
\ No newline at end of file
+[状态机模式](fsm/fsm_manual_zh.md)
\ No newline at end of file