前言
在分布式系统中,分布式事务是一个必须要解决的问题,目前使用较多的是最终一致性方案。自年初阿里开源了Fescar(四月初更名为Seata)后,该项目受到了极大的关注度,目前已接近8000Star。Seata以高性能和零侵入的方式为目标解决微服务领域的分布式事务难题,目前正处于快速迭代中,近期小目标 是生产可用的Mysql版本。关于Seata的总体介绍,可以查看官方WIKI获得更多更全面的内容介绍。
本文主要基于spring cloud+spring jpa+spring cloud alibaba fescar+mysql+seata的结构,搭建一个分布式系统的demo,通过seata的debug日志和源代码,从client端(RM、TM)的角度分析说明其工作流程及原理。
文中代码基于fescar-0.4.1,由于项目刚更名为seata不久,例如一些包名、类名、jar包名称还都是fescar的命名,故下文中仍使用fescar进行表述。
示例项目:https://github.com/fescar-group/fescar-samples/tree/master/springcloud-jpa-seata
相关概念
- XID:全局事务的唯一标识,由ip:port:sequence组成
- Transaction Coordinator (TC):事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚
- Transaction Manager (TM ):控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议
- Resource Manager (RM):控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚
分布式框架支持
Fescar使用XID表示一个分布式事务,XID需要在一次分布式事务请求所涉的系统中进行传递,从而向feacar-server发送分支事务的处理情况,以及接收feacar-server的commit、rollback指令。 Fescar官方已支持全版本的dubbo协议,而对于spring cloud(spring-boot)的分布式项目社区也提供了相应的实现
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-fescar</artifactId>
<version>2.1.0.BUILD-SNAPSHOT</version>
</dependency>
该组件实现了基于RestTemplate、Feign通信时的XID传递功能。
业务逻辑
业务逻辑是经典的下订单、扣余额、减库存流程。 根据模块划分为三个独立的服务,且分别连接对应的数据库
- 订单:order-server
- 账户:account-server
- 库存:storage-server
另外还有发起分布式事务的业务系统
- 业务:business-server
项目结构如下图
正常业务
- business发起购买请求
- storage扣减库存
- order创建订单
- account扣减余额
异常业务
- business发起购买请求
- storage扣减库存
- order创建订单
- account
扣减余额异常
正常流程下2、3、4步的数据正常更新全局commit,异常流程下的数据则由于第4步的异常报错全局回滚。
配置文件
fescar的配置入口文件是registry.conf,查看代码ConfigurationFactory得知目前还不能指定该配置文件,所以配置文件名称只能为registry.conf
private static final String REGISTRY_CONF = "registry.conf";
public static final Configuration FILE_INSTANCE = new FileConfiguration(REGISTRY_CONF);
在registry
中可以指定具体配置的形式,默认使用file类型,在file.conf中有3部分配置内容
- transport transport部分的配置对应NettyServerConfig类,用于定义Netty相关的参数,TM、RM与fescar-server之间使用Netty进行通信
- service
service {
#vgroup->rgroup
vgroup_mapping.my_test_tx_group = "default"
#配置Client连接TC的地址
default.grouplist = "127.0.0.1:8091"
#degrade current not support
enableDegrade = false
#disable
是否启用seata的分布式事务
disableGlobalTransaction = false
}
- client
client {
#RM接收TC的commit通知后缓冲上限
async.commit.buffer.limit = 10000
lock {
retry.internal = 10
retry.times = 30
}
}