支付宝分布式事务服务DTS二
原文链接:https://blog.csdn.net/qq_27384769/article/details/79305133
典型场景和实现原理
首先来看一个典型的分布式事务场景
在这个例子中,app1 作为分布式事务的发起方调用了参与者 app2 的 insert 操作和 app3 的 update 操作,之后调用自己的本地 insert 操作,在这个分布式事务中包含了 3 次对 db 的操作,而 3 个 db 分属于不同的系统,图中虚线覆盖的范围是 app1 的一个本地事务模版的代码范围。
来看看在 DTS 内部针对这个场景是如何实现的。
app2 和 app3 作为参与者分别实现了 prepare,commit 和 rollback 接口
我们将 prepare 阶段称为第一阶段,commit/rollback 阶段称为第二阶段
上图就是典型的 DTS 事务流转示例,第一阶段被包含在发起方的本地事务模版中,发起方的本地事务结束后,开始执行二阶段操作,二阶段结束,DTS 事务整个结束。
针对以上运行流程,我们可以总结如下
DTS 分布式事务是基于两阶段提交( 2 phase commit,简称 2pc)原理
事务发起方是分布式事务的协调者
事务发起方本地事务的最终状态(提交或回滚)决定整个分布式事务的最终状态
分布式事务必须在本地事务模板中进行
参与者通过配置在 xml 中的拦截器来完成 action 信息的获取和数据插入
事务参与者的接口需要支持两阶段。发起方(使用者)只关注第一阶段的方法,第二阶段由框架自动调用。
再来看看以上的说明对应的发起方代码是怎样的
对应的参与者接口是这样的
最核心的地方在于 prepare 接口打上了@TwoPhaseBusinessAction 标注,通过这个标注DTS 框架可以感知到这个服务就是一个 DTS 的参与者
接下来,结合上面这个例子让我们详细分析下 DTS 内部的工作原理,大体如下图
核心点:
使用数据库持久化记录事务数据,且使用独立的事务模版,也就是单独事务
特别关注红线对应的 sql,这是一句 update 主事务表的 sql,而这句 sql 是在发起方的本地事务中的,这样一来就和发起方的事务绑定了,如果发起方本地事务成功,则这句 update 语句必然成功,如果发起方本地事务失败,则这句 update 语句必然失败,这样我们就可以根据 activity 表的事务记录的状态来决定这笔分布式最终的状态是成功还是失败了
在调用参与者前,启动单独事务插入代表这个参与者的分支事务记录,以供后续恢复使用
二阶段是通过 spring 提供的事务同步器实现的,如果发起方的本地事务失败,则二阶段自动回滚所有参与者,如果发起方的本地事务成功,则二阶段自动提交所有参与者。二阶段结束后,删除所有事务记录
事务恢复
学习了 DTS 的原理之后,可能你会问,如果二阶段失败会怎样?比如需要 commit app2 和 app3,如果 commit app2 的时候断电了,这笔事务数据是否还能正常提交?答案是肯定的,通过我们的 xts-server 这个恢复系统来保证事务一定会被提交/回滚。在某些特殊情况下(比如断电,jvm crash等导致分布式事务没有处理完就结束了),xts-server 靠持久化记录到 db 的事务数据来完成恢复
恢复系统的特点
恢复系统需要配置所有参与者信息,比如参与者的名称,全类名以及提交和回滚方法名
恢复系统需要连接发起方的数据库,来获取对应的事务数据
恢复系统是定时恢复的,每隔一分钟从发起方的数据库获取一次数据
恢复系统获取的数据都是一分钟之前待处理的数据,这个一分钟是一个经验值,我们认为 99.9999% 的分布式事务一分钟就应该结束了,事实也确实如此
嵌套事务支持
在前面的典型场景里是 A->B 单层调用的关系,随着业务越来越复杂,可能会出现 A->B->C 的嵌套场景,在这个场景下,A 仍然是作为事务的发起方,我们把 B 称为嵌套参与者,C 为普通参与者,如果所示
可以看到,一阶段和二阶段的调用链路是完全一致的,需要注意的是对于嵌套参与者 B 来说需要 DB 资源来存放下游参与者(这里是 C)的分支事务记录,在 B 调用 C 的一阶段的时候会记录代表 C 的分支事务记录,在二阶段 XTS 框架在提交完 B 这个参与者之后,会捞取 B 的分支事务表,找到 C 的记录,从而发起对 C 的二阶段提交
例子
我们假设 A 系统提供一个充值服务,调用 B 系统,B 系统再调用 C 系统完成充值,对于 A 系统发起方代码看起来是这样的
来看看 B 这个嵌套参与者的接口,和普通参与者没什么差别
看看对应的实现
可以看到对应 B 参与者来说代码里并没有什么特殊的点,那么 XTS 框架是如何做到在提交 B 的时候自动提交 C 的呢?答案就是拦截器,对于嵌套参与者我们需要配置一个特殊的拦截器NestedBusinessActionInterceptor
看看 NestedBusinessActionInterceptor 的配置
NestedBusinessActionInterceptor 作用于 B 服务端拦截 B 的分布式服务,和 A 系统没关系
前面提到对于嵌套参与者需要提供 DB 资源来存储下游的分支事务记录,所以对于 B 系统也需要配置 BusinessActivityControlService 来让 XTS 框架感知 B 的 DB 信息
总结
对于嵌套参与者的使用:
需要提供 DB 资源,来让 XTS 框架持久化分支事务记录
配置 NestedBusinessActionInterceptor 拦截器