# 分布式事务
## 说明
Zebra 微服务框架集成了 [Seata](https://github.com/seata/seata) 的 TCC 模式用于支持分布式事务。
虽然 Zebra 微服务框架提供了分布式事务组件,但是仍需开发者实现各种异常处理(如空回滚、幂等、悬挂),实现一个较为完备分布式事务复杂度也还是不低,请确定是否确实要使用分布式事务。
## 样例
请参考 [https://gitee.com/gszebra/zebra/tree/master/zebra-sample/distributed-transaction](https://gitee.com/gszebra/zebra/tree/master/zebra-sample/distributed-transaction)
## 开发
### 引入依赖
引入如下依赖
```markup
<dependency>
<groupId>com.guosen</groupId>
<artifactId>zebra-distributed-transaction</artifactId>
<version>${zebra.version}</version>
</dependency>
```
### 配置
#### 配置样例
```text
zebra.distributed.transaction.service.default.grouplist=seataServerIP:seataServerPort
```
#### 配置说明
| 配置项 | 说明 |
| :--- | :--- |
| zebra.distributed.transaction.service.default.grouplist | Seata Server 服务器地址信息,格式为 IP:PORT |
### 代码编写
#### RM 资源的封装
对于分布式事务用到的各种资源,比如 DB 和远程微服务调用。必须定义一个 LocalTCC 接口给包装起来。
```java
@LocalTCC
public interface BusBookingService {
@TwoPhaseBusinessAction(name = "BusTccAction" , commitMethod = "commit", rollbackMethod = "rollback")
boolean prepare(BusinessActionContext actionContext,
@BusinessActionContextParameter(paramName = "busBooking") BusBooking busBooking);
/**
* Commit boolean.
*
* @param actionContext the action context
* @return the boolean
*/
boolean commit(BusinessActionContext actionContext);
/**
* Rollback boolean.
*
* @param actionContext the action context
* @return the boolean
*/
boolean rollback(BusinessActionContext actionContext);
}
```
如上图,在接口添加如下注解
* @LocalTCC,在接口类上添加
* @TwoPhaseBusinessAction,在 TCC 中的 Try 函数(Java 中 try 是关键字,所以样例中函数名为 prepare)上添加,同时此注解必须设置三个属性。
| 属性名称 | 说明 |
| :--- | :--- |
| name | TCC bean 名称,必须确保唯一 |
| commitMethod | TCC 中 Commit 步骤要调用的此接口的函数名称 |
| rollbackMethod | TCC 中 Cancel 步骤要调用的此接口的函数名称 |
同时,commit 和 cancel 对应的函数返回类型必须为 boolean,以便 TCC 框架知道 Commit 或者 Cancel 的结果。
* @BusinessActionContextParameter,如果后续的 commit/rollback 函数要获取到传递给 prepare 函数的参数,则在 prepare 函数的参数上添加此注解。
#### 全局事务的封装
```java
@Autowired
private BusBookingService busBookingService;
@Autowired
private HotelBookingService hotelBookingService;
/**
* 添加GlobalTransactional注解,以便开启分布式事务
* 必须在实现类上面加,不能在接口上加。
*/
@GlobalTransactional
@Override
public void booking() {
LOGGER.info("Begin booking");
String xid = RootContext.getXID();
LOGGER.info("xid is : {}", xid);
BusBooking busBooking = new BusBooking();
busBooking.setBusId("bus001");
busBooking.setUserId("user001");
HotelBooking hotelBooking = new HotelBooking();
hotelBooking.setHotelId("hotel001");
hotelBooking.setUserId("user001");
// 调用TCC各个服务的prepare方法
// TCC在prepare执行完毕后,如果全部prepare都成功,则调用对应的commit函数(一直尝试直到所有commit成功)
// 如果有部分prepare失败,则调用对应的rollback函数(一直尝试直到所有rollback成功)
BusinessActionContext actionContext = new BusinessActionContext();
busBookingService.prepare(actionContext, busBooking);
hotelBookingService.prepare(actionContext, hotelBooking);
LOGGER.info("Finish booking");
}
```
如上图,在**实现类**(注意是**类**而非**接口**)的函数添加 @GlobalTransactional。和普通 Spring 一样,使用 @Autowired 注入加了 @LocalTCC 的接口,然后在 @GlobalTransactional 注解的函数里调用它们的 prepare 函数。
TCC 会为加了 @GlobalTransactional 注解的函数的类生成代理,当此函数被调用时,开启分布式事务。
### TCC 异常处理
请参考文章[分布式事务 Seata TCC 模式深度解析](https://blog.csdn.net/huaishu/article/details/89880971)的第三节“TCC 异常控制”。
> 在有了一套完备的 TCC 接口之后,是不是就真的高枕无忧了呢?答案是否定的。在微服务架构下,很有可能出现网络超时、重发,机器宕机等一系列的异常 Case。一旦遇到这些 Case,就会导致我们的分布式事务执行过程出现异常。根据蚂蚁金服内部多年的使用来看,最常见的主要是这三种异常,分别是空回滚、幂等、悬挂。
>
> 因此,TCC 接口里还需要解决这三类异常。实际上,这三类问题可以在 Seata 框架里完成,只不过我们现在的 Seata 框架还不具备,之后我们会把这些异常 Case 的处理移植到 Seata 框架里,业务就无需关注这些异常情况,专注于业务逻辑即可。
>
> 虽然业务之后无需关心,但是了解一下其内部实现机制,也能更好的排查问题……
- 简介
- 入门
- Docker方式
- 手工方式
- 环境搭建
- 简述
- 初始化服务器
- 安装 Etcd
- 初始化数据库
- 安装配置中心
- 安装服务中心
- 安装监控中心
- 安装API网关
- 开发第一个微服务
- 简述
- 准备工作
- 接口定义
- 实现
- 配置
- 部署
- 验证
- 微服务开发
- 服务调用
- 服务上下文
- 日志
- 高级特性
- 范化调用
- 异步调用
- ACL 控制
- 慢启动
- 消息大小
- 组件使用
- 简述
- 缓存
- MyBatis
- RocketMQ
- 分库分表
- 读写分离
- 分布式事务
- 分布式锁
- 运维管理
- 服务依赖
- 服务查询
- 服务方法查询
- 配置中心
- API网关配置
- 流量控制
- 简述
- 限流
- 熔断
- 系统保护
- 白名单
- 黑名单
- 监控
- 主动探测
- 调用链
- 日志开关
- 最佳实践
- 常见问题
- 深入 Zebra
- 架构
- 通信协议
- 路标