分布式事务中Tcc模式常见问题解决
TCC的异常场景
在分布式系统中,随时随地都需要⾯对⽹络超时,⽹络重发和服务器宕机等问题。所以分布式事务框架作为搭载在分布式系统之上的⼀个框架型应⽤也绕不开这些问题。具体⽽⾔,有以下常见问题:
1. 幂等处理
2. 空回滚
3. 资源悬挂
这些异常的应对需要TCC框架的⽀持和解决⽅案。
练武术幂等处理
产⽣原因
因为⽹络抖动等原因,分布式事务框架可能会重复调⽤同⼀个分布式事务中的⼀个分⽀事务的⼆阶段接⼝。所以分⽀事务的⼆阶段接⼝Confirm/Cancel需要能够保证幂等性。如果⼆阶段接⼝不能保证幂等性,
则会产⽣严重的问题,造成资源的重复使⽤或者重复释放,进⽽导致业务故障。(多次执⾏cancel confirm 重试操作)
从上图中红⾊部分可以看到:如果当TC调⽤参与者的⼆阶段⽅法时,发⽣了异常(TC本⾝异常或者⽹络异常丢失结果)。此时TC⽆法感知到调⽤的结果。为了保证分布式事务能够⾛到终态,此时TC会按照⼀定的规则重复调⽤参与者的⼆阶段⽅法。
应对策略
骆驼祥子读书心得600字对于幂等类型的问题,通常的⼿段是引⼊幂等字段进⾏防重放攻击。对于分布式事务框架中的幂等问题,同样可以祭出这⼀利器。我们可以通过增加⼀张事务状态控制表来实现,这个表的关键字段有以下⼏个:
1. 主事务ID
2. 分⽀事务ID
3. 分⽀事务状态
其中1和2构成表的联合主键来唯⼀标识⼀笔分布式事务中的⼀条分⽀事务。3⽤来标识该分⽀事务的状态,⼀共有3种状态:
1. INIT(I) - 初始化
2. CONFIRMED© - 已提交
3. ROLLBACKED® - 已回滚
喊的组词幂等记录的插⼊时机是参与者的Try⽅法,此时的分⽀事务状态会被初始化为INIT。然后当⼆阶段的Confirm/Cancel执⾏时会将其状态置为CONFIRMED/ROLLBACKED。
当TC重复调⽤⼆阶段接⼝时,参与者会先获取事务状态控制表的对应记录查看其事务状态。如果状态已经为
勤奋学习演讲稿CONFIRMED/ROLLBACKED,那么表⽰参与者已经处理完其分内之事,不需要再次执⾏,可以直接返回幂等成功的结果给TC,帮助其推进分布式事务。增加了幂等记录的写⼊和读取判断后,时序图如下(蓝⾊部分):
空回滚
产⽣原因
先来说定义,当没有调⽤参与⽅Try⽅法的情况下,就调⽤了⼆阶段的Cancel⽅法,Cancel⽅法需要有办法识别出此时Try有没有执⾏。如果Try还没执⾏,表⽰这个Cancel操作是⽆效的,即本次Cancel属于空回滚;如果Try已经执⾏,那么执⾏的是正常的回滚逻辑。
逆回购操作
如上图所⽰,红⾊部分的⼀阶段Try可能失败。
⾸先发起⽅在调⽤参与者之前,会向TC申请开始⼀笔分布式事务。然后发起⽅调⽤参与者的⼀阶段⽅法,在调⽤实际发⽣之前,⼀般会有切⾯拦截器感知到此次Try调⽤,然后写⼊⼀条分⽀事务记录。紧接着,在实际调⽤参与者的Try⽅法时发⽣了异常。异常原因可以是发起⽅宕机,⽹络抖动等。
总⽽⾔之,就是Try⽅法没有执⾏成功,然⽽此时这笔分布式事务和分⽀事务已经落库。有两种情况会触发分布式事务的回滚:
1. 发起⽅认为当前分布式事务⽆法成功,主动通知TC回滚
七宝镇2. TC发现分布式事务超时,被动触发回滚
触发回滚操作后,TC会对该分布式事务关联的分⽀事务调⽤其⼆阶段Cancel。在执⾏Cancel时,Try
还未执⾏成功,触发空回滚。如果不对空回滚加以防范的话,可能会造成资源的⽆效释放。即在没有预留资源的情况下就释放资源,造成故障。
应对策略
可以发现,要应对空回滚的问题,就需要让参与者在⼆阶段的Cancel⽅法中有办法识别到⼀阶段的Try是否已经执⾏。
很显然,可以继续利⽤事务状态控制表来实现这个功能。
汤潮前⾯提到过为了保证幂等性,当Try⽅法被成功执⾏后,会插⼊⼀条记录,标识该分⽀事务处于INIT状态。所以后续当⼆阶段的Cancel⽅法被调⽤时,可以通过查询控制表的对应记录进⾏判断。如果记录存在且状态为INIT,就表⽰⼀阶段已成功执⾏,可以正常执⾏回滚操作,释放预留的资源;如果记录不存在则表⽰⼀阶段未执⾏,本次为空回滚,不释放任何资源。
时序图如下所⽰:
资源悬挂(try - cancel --try 循环执⾏,cancel在try之前)
产⽣原因
航天纪念钞悬挂,顾名思义,是有⼀些资源被悬挂起来后续⽆法处理了。那么什么场景下才会出现这种现象呢?
上⼀节中提到过空回滚,指的是当⼀阶段Try未执⾏成功,⽽⼆阶段Cancel就因TC回滚整个分布式事务⽽被调⽤。
但是考虑⼀种极端情况,当分布式事务到终态后,参与者的⼀阶段Try才被执⾏,此时参与者会根据业务需求预留相关资源。预留资源只有当前事务才能使⽤,然⽽此时分布式事务已经⾛到终态,后续再没有任何⼿段能够处理这些预留资源。⾄此,就形成了资源悬挂。
这种⼀阶段⽐⼆阶段执⾏的还晚的情况看似不可能,但是仔细考虑RPC调⽤的时序,其实这种情况在复杂多变的⽹络中是完全可能的,下⾯的时序展⽰了这种可能性:
1. 发起⽅通过RPC调⽤参与者⼀阶段Try,但是发⽣⽹络阻塞导致RPC超时
2. RPC超时后,TC会回滚分布式事务(可能是发起⽅主动通知TC回滚或者是TC发现事务超时后回滚),调⽤已注册的各个参与⽅的⼆阶
段Cancel
3. 参与⽅空回滚后,发起⽅对参与者的⼀阶段Try才开始执⾏,进⾏资源预留从⽽形成悬挂
使⽤时序图来描述,红⾊部分为产⽣资源悬挂的关键步骤:
应对策略
资源悬挂的本质原因在于,⼀阶段和⼆阶段的执⾏顺序没有被严格地保证。所以相应的解决⽅案还是通过读取事务状态控制表的事务状态。前⾯在幂等⽅案的讨论中说过:
幂等记录的插⼊时机是参与者的Try⽅法,此时的分⽀事务状态会被初始化为INIT。然后当⼆阶段的Confirm/Cancel执⾏时会将其状态置为CONFIRMED/ROLLBACKED。
由于悬挂的产⽣背景是⼀阶段⽅法根本就未执⾏,所以此时事务控制记录是不存在的,需要在⼆阶段中处理ROLLBACK的情况(因为超时后触发回滚不可能存在⼆阶段为CONFIRM)。
处理⽅案为在判断为空回滚的场景下(体现在对应⼀阶段事务控制记录不存在),插⼊⼀条状态为ROLLBACKED的控制记录。
那么下次当⼀阶段Try抵达执⾏的时候,⾸先会尝试插⼊状态为INIT的事务控制记录。如果插⼊失败,表⽰当前分⽀事务的记录已经存
在,Try⽆需继续执⾏。有⼏种可能性会导致此情形:
1. ⼀阶段Try重复请求,⽹络抖动情况可能发⽣,可以理解为命中幂等
2. ⼆阶段插⼊了防悬挂记录,⼀阶段不可继续执⾏