简介
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。
Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
角色
Seata 的设计思路是将一个分布式事务可以理解成一个全局事务,下面挂了若干个分支事务,而一个分支事务是一个满足 ACID 的本地事务,因此我们可以操作分布式事务像操作本地事务一样。
01
三个组件
Seata 内部定义了 3个模块来处理全局事务和分支事务的关系和处理过程,这三个组件分别是:
Transaction Coordinator (TC): 事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚。
Transaction Manager (TM): 控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议。
Resource Manager (RM): 控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚。
02
执行步骤
简要说说整个全局事务的执行步骤:
- TM 向 TC 申请开启一个全局事务,TC 创建全局事务后返回全局唯一的 XID,XID 会在全局事务的上下文中传播;
- RM 向 TC 注册分支事务,该分支事务归属于拥有相同 XID 的全局事务;
- TM 向 TC 发起全局提交或回滚;
- TC 调度 XID 下的分支事务完成提交或者回滚。
支持模式
01
Seata AT 模式
两阶段提交协议的演变:
一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
二阶段:
提交异步化,非常快速地完成。
回滚通过一阶段的回滚日志进行反向补偿。
02
Seata TCC 模式
整体是两阶段提交的模型。
全局事务是由若干分支事务组成的,分支事务要满足两阶段提交的模型要求,即需要每个分支事务都具备自己的:
一阶段 prepare 行为
二阶段 commit 或 rollback 行为
03
Seata Saga 模式
目前SEATA提供的Saga模式是基于状态机引擎来实现的,机制是:
- 通过状态图来定义服务调用的流程并生成 json 状态语言定义文件。
- 状态图中一个节点可以是调用一个服务,节点可以配置它的补偿节点。
- 状态图 json 由状态机引擎驱动执行,当出现异常时状态引擎反向执行已成功节点对应的补偿节点将事务回滚。
- 可以实现服务编排需求,支持单项选择、并发、子流程、参数转换、参数映射、服务执行状态判断、异常捕获等功能。
04
Seata XA 模式
利用事务资源(数据库、消息服务等)对 XA 协议的支持,以 XA 协议的机制来管理分支事务的一种 事务模式。
执行阶段:
- 可回滚:业务 SQL 操作放在 XA 分支中进行,由资源对 XA 协议的支持来保证 可回滚。
- 持久化:XA 分支完成后,执行 XA prepare,同样,由资源对 XA 协议的支持来保证持久化(即,之后任何意外都不会造成无法回滚的情况)。
完成阶段:
- 分支提交:执行 XA 分支的 commit
- 分支回滚:执行 XA 分支的 rollback
源码分享
01
代码整体结构
seata-common
提供一些工具类,异常等。
seata-core
对rpc,消息格式,序列化扩展点,eventbus等做了些封装。
seata-config
配置中心接入,对naocs,apollo,etcd,zk,file等做了封装。
seata-discovery
对负载均衡,服务注册发现做了封装,接入nacos,zk,etcd,eureka等。
seata-server
coordinator 处理事务协调逻辑
lock 全局锁处理逻辑
store 存储分布式锁
seata-rm
rm的实现核心逻辑封装。
seata-tm
tm的核心实现逻辑封装。
seata-tcc
tcc事务模式扩展。
seata-spring
seata spring支持,比如@GlobalLock,@GlobalTransactional就是在这里支持。
seata-integration
对各种rpc支持,比如xid透传就在里面实现的。
02
RPC
- AbstractNettyRemoting:Remoting 类的最顶层抽象,包含了服务端和客户端公用的成员变量与公用方法
- RemotingClient:客户端最顶级接口
- RemotingServer:服务端最顶级接口
- AbstractNettyRemotingClient:客户端抽象类,继承AbstractNettyRemoting 类并实现了 RemotingClient接口
- NettyRemotingServer:服务端实现类,继承AbstractNettyRemoting 类并实现了RemotingServer接口
- RmNettyRemotingClient:Rm客户端实现类,继承 AbstractNettyRemotingClient类
- TmNettyRemotingClient:Tm客户端实现类,继承AbstractNettyRemotingClient类
- 服务端和客户端启动引导类
*seata server与tm,rm交互图
针对以上交互图,
seata AbstractNettyRemoting有一个processMessage,
里面定义了processorTable。对于不同请求,采用不同的RemotingProcessor去处理请求逻辑,成功的将业务逻辑从netty Netty ChannelHadler解耦出来。
processor主要有以下这些。
03
AT流程
入口GlobalTransactionScanner,主要完成TMClient, RMClient与Seata Server建立连接。
第一阶段
核心是对业务sql进行解析,转换成undolog,并同时入库。
GlobalTransactionalInterceptor会对@GlobalTransactional注解进行拦截。
里面会去调用TransactionalTemplate#execute,进行具体逻辑处理。
主要是有以下几步:
开启全局事务beginTransaction(txInfo, tx)
执行具体业务逻辑business.execute()
提交事务commitTransaction(tx)
如果有异常则进行回滚操作completeTransactionAfterThrowing(txInfo, tx, ex)
StatementProxy解析sql,生成undolog,执行业务流程,分支事务注册到seata server 整个流程图。
executeAutoCommitTrue中先将autoCommit设置为false,原因是要对sql进行解析,生成undolog在一个事务中入库。
ConnectionProxy#doCommit
如果处于全局事务中,则调用processGlobalTransactionCommit()处理全局事务提交。
如果加了全局锁@GlobalLock注解,processLocalCommitWithGlobalLocks()加全局锁并提交。
如果没有任何注释,按直接进行事务提交。
第二阶段
分布式事务操作成功,则TC通知RM异步删除undolog。
RM具体处理回调逻辑在RMHandlerAT。
getResourceManager()会用到DataSourceManager,这个是利用SPI机制扩展的。
里面是利用AsyncWorker异步执行。
处理逻辑主要分以下几步骤:
- 先按resourceId(数据连接)对提交操作进行分组,同个数据库的可以一起操作,提升效率。
- 根据resourceId找到对应DataSourceProxy,并获取数据库连接getPlainConnection()。
- 调用 UndoLogManagerFactory.getUndoLogManager(dataSourceProxy.getDbType()).batchDeleteUndoLog(xids,branchIds, conn)删除undolog。
分布式事务操作失败,TM向TC发送回滚请求,RM 收到协调器TC发来的回滚请求,通过 XID 和 Branch ID 找到相应的回滚日志记录,通过回滚记录生成反向的更新 SQL 并执行,以完成分支的回滚。
具体的实现逻辑在AbstractUndoLogManager#undo中可以看到。
处理逻辑主要分以下几步骤:
- 根据xid,branchId找到具体的undolog
- 从undolog beforeImg解析回滚sql执行
- 删除undolog,提交
04
其他
TCC模块扩展了@TwoPhaseBusinessAction,RMHandlerTCC等几个点
XA模块扩展了DataSourceProxyXA,StatementProxyXA,ConnectionProxyXA等。
优缺点及总结
- 对于强一致性要求高的场景使用会比较方便。
- AT模式会利用lock_table锁住具体行,如果分支事务较多,某个分支事务执行较慢,会影响整体性能。
- TCC模式相对AT模式会可控些,因为AT模式回滚镜像是由代码自动生成,也不会有AT模式下锁住某一行的问题,但是需要整个链路上下游都进行相应改造,相对改造成本会较大。
- XA模式
数据在整个事务处理过程结束前,都被锁定,读写都按隔离级别的定义约束起来,XA prepare 后,分支事务进入阻塞阶段,收到 XA commit 或 XA rollback 前必须阻塞等待。
性能的损耗主要来自两个方面:一方面,事务协调过程,增加单个事务的 RT;另一方面,并发事务数据的锁冲突,降低吞吐。
- 对Seata Server可用性要求很高,一次分布式事务RM,TM和TC会有多次交互,对服务稳定性要求也比较高,1.3版本后支持了自动降级,但是降级后,强一致性又保证不了了。
- 详细的使用例子可参考:https://github.com/seata/seat...
【END】
文|Leo