Seata 是什么
Seata
是阿里近期开源的分布式事务框架,地址:https://github.com/seata/seata。框架包括了集团的 TXC
(云版本叫 GTS
)和蚂蚁金服的 TCC
两种模式,短短数月 Github
上的 star
数已经接近一万,算是目前唯一有大厂背书的分布式事务解决方案。
TXC
在 Seata
中又叫 AT
模式,意为补偿方法是框架自动生成的,对用户完全屏蔽,用户可以向使用本地事务那样使用分布式事务,缺点是仅支持关系型数据库(目前支持 MySQL
),引入 Seata AT
的服务需要本地建表存储 rollback_info
,隔离级别默认 RU
适用场景有限。
TCC
不算是新概念,很早就有了,用户通过定义 try/confirm/cancel
三个方法在应用层面模拟两阶段提交,区别在于 TCC 中 try
方法也需要操作数据库进行资源锁定,后续两个补偿方法由框架自动调用,分别进行资源提交和回滚,这点同单纯的存储层 2PC
不太一样。蚂蚁金服向 Seata
贡献了自己的 TCC
实现,据说已经演化了十多年,大量应用在在金融、交易、仓储等领域。
分布式事务的诞生背景
早期应用都是单一架构,例如支付服务涉及到的账户、金额、订单系统等都由单一应用负责,底层访问同一个数据库实例,自然事务操作也是本地事务,借助 Spring
可以轻松实现;但是由于量级越来越大,单一服务需要进行职责拆分变为三个独立的服务,通过 RPC
进行调用,数据也存在不同的数据库实例中,由于这时一次业务操作涉及对多个数据库数据的修改,无法再依靠本地事务,只能通过分布式事务框架来解决。
TCC 就是分布式事务的一种解决方案,属于柔性补偿型,优点在于理解简单、仅 try
阶段加锁并发性能较好,缺点在于代码改造成本。
什么是 TCC 本文就不再赘述了,TCC 的概念本身并不复杂
Seata TCC 使用方法
在分析源码之前,我们先简要提及下 Seata TCC
模式的使用方法,有助于后续理解整个 TCC
流程。
Seata TCC 参与方
Seata
中的 TCC
模式要求 TCC
服务的参与方在接口上增加 @TwoPhaseBusinessAction
注解,注明 TCC
接口的名称(全局唯一),TCC
接口的 confirm
和 cancel
方法的名称,用于后续框架反射调用,下面是一个 TCC
接口的案例:
public interface TccAction {
@TwoPhaseBusinessAction(name = "yourTccActionName", commitMethod = "confirm", rollbackMethod = "cancel")
public boolean try(BusinessActionContext businessActionContext, int a, int b);
public boolean confirm(BusinessActionContext businessActionContext);
public boolean cancel(BusinessActionContext businessActionContext);
}
紧接着定义实现类 Impl
实现这个接口,为三个方法提供具体实现。最后将参与方服务进行发布,注册到远端,主要为了后续能让 Seata
框架调用到参与方的 confirm
或者 cancel
方法闭环整个 TCC
事务。
Seata TCC 发起方
Seata TCC
的发起方类似于我们上图中的 payment service
,参与方需要在业务方法上增加 @GlobalTransactional
注解,用于开启切面注册全局事务,业务方法中调用 TCC
参与方的若干 try
方法,一旦业务方法调用成功,Seata
框架会通知 TC
回调这些参与方的 confirm
和 cancel
方法。
源码分析
Seata
中 TCC
模式的源码并不复杂,主要集中于:
module | class | 功能 |
---|---|---|
seata-spring | GlobalTransactionalInterceptor.class | 全局事务切面逻辑,包括注册全局事务,拿到 xid |
seata-spring | TccActionInterceptor.class | TCC 参与方切面逻辑 |
seata-tcc | TCCResourceManager.class | 解析 TCC Bean,保存 TCC Resources,便于后续回调 |
seata-tcc | ActionInterceptorHandler.class | TCC 分支事务注册实现 |
seata-server | DefaultCoordinator.class、FileTransactionStoreManager.class | 主要是 TC 的实现、事务存储等实现 |
注册 TCC Resources
Seata
中一个 TCC
接口被称作一个 TCC Resources
,其结构如下:
public class TCCResource implements Resource {
private String resourceGroupId = "DEFAULT";
private String appName;
private String actionName; // TCC 接口名称
private Object targetBean; // TCC Bean
private Method prepareMethod; // try 方法
private String commitMethodName;
private Method commitMethod; // confirm 方法
private String rollbackMethodName;
private Method rollbackMethod; // cancel 方法
// …… 省略
}
Seata
解析到应用中存在 TCC Bean
,则通过 parserRemotingServiceInfo
方法生成一个 TCCResource
对象,进而调用 TCCResourceManager
类的 registerResource
方法,将 TCCResource
对象保存到本地的 tccResourceCache
中,它是一个 ConcurrentHashMap
结构,同时通过 RmRpcClient
将该 TCCResource
的 resourceId
、address
等信息注册到服务端,便于后续 TC
通过 RPC
回调到正确的地址。
// 解析 TCCResource 的部分代码
Class<?> interfaceClass = remotingBeanDesc.getInterfaceClass();
Method[] methods = interfaceClass.getMethods();
if(isService(bean, beanName)){
try {
// 如果是 TCC service Bean,解析并注册该 resource
Object targetBean = remotingBeanDesc.getTargetBean();
for(Method m : methods){
TwoPhaseBusinessAction twoPhaseBusinessAction = m.getAnnotation(TwoPhaseBusinessAction.class);
if(twoPhaseBusinessAction != null){
// 如果有 TCC 参与方注解,定义一个 TCCResource,
TCCResource tccResource = new TCCResource();
tccResource.setActionName(twoPhaseBusinessAction.name());
// TCC Bean
tccResource.setTargetBean(targetBean);
// try 方法
tccResource.setPrepareMethod(m);
// confirm 方法名称
tccResource.setCommitMethodName(twoPhaseBusinessAction