bluetears 2019-04-26
在微服务架构中,通常一个业务操作,伴随着多个子任务,比如创建订单业务操作,涉及到下单、扣减库存、下发仓储物流、邮件/短信通知等等。那如何保证实现一致性呢?
又称为Long-running-transaction(长事务),核心思想是把一个长事务分为多个本地事务来完成,由一个Process Manager 统一协调。如果成功,则继续往下执行,如果失败,则调用补偿操作。
每个业务都至少需要实现正向、反向两个接口。
我们看如下一个业务场景。在购买旅游套餐业务操作涉及到三个操作,他们分别是预定车辆,预定宾馆,预定机票,他们分别属于三个不同的远程接口。可能从我们程序的角度来说他们不属于一个事务,但是从业务角度来说是属于同一个事务的。
他们的执行顺序如上图所示,所以当发生失败时,会依次进行取消的补偿操作。
因为长事务被拆分了很多个业务流,所以 Saga 事务模型最重要的一个部件就是流程管理器(Process Manager)。
在执行到第3步时,发生了失败,回退的过程也比较复杂,特别是子业务比较多的场景。
如何实现数据的一致性呢?如下罗列了一些方案。
1. 建立一个定时任务去检查数据的完整性,如果第3步失败了,定时任务会检测到并修复数据。
2. 如果失败,可以发生消息到MQ,消费者根据状态,去重试,或者执行回退操作。同时,也可以进一步检查数据的一致性。
3. 本地操作日志或DB日志。
TCC(Try Confirm Cancel)事务模型的思想和2PC提交有点类似。下图是TCC和2PC(XA)的对比。
1) 在阶段1:
在2PC(XA)中,各个RM准备提交各自的事务分支,事实上就是准备提交资源的更新操作(insert、delete、update等);而在TCC中,是主业务活动请求(try)各个从业务服务预留资源。
2) 在阶段2:
2PC(XA)根据第一阶段每个RM是否都prepare成功,判断是要提交还是回滚。如果都prepare成功,那么就commit每个事务分支,反之则rollback每个事务分支。
TCC中,如果在第一阶段所有业务资源都预留成功,那么confirm各个从业务服务,否则取消(cancel)所有从业务服务的资源预留请求。
TCC两阶段提交与2PC/XA两阶段提交的区别是:
TCC 把相关的操作,从数据库转移到业务中,以此降低数据库的压力,并且不需要加速,性能也得到了提升。
TCC 其实就是采用的补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。它分为三个阶段:
举个例子,假入 Bob 要向 Smith 转账,思路大概是:
我们有一个本地方法,里面依次调用:
1、首先在 Try 阶段,要先调用远程接口把 Smith 和 Bob 的钱给冻结起来。
2、在 Confirm 阶段,执行远程调用的转账的操作,转账成功进行解冻。
3、如果第2步执行成功,那么转账成功,如果第二步执行失败,则调用远程冻结接口对应的解冻方法 (Cancel)。
TCC的优势:
在业务层处理,降低数据库的压力;
比2PC性能好很多,没有真正在数据库加锁;
TCC的问题:
增加业务复杂度,需要提供相应的Try、Confirm、Cancel接口;
需要提供幂等性实现;
TCC有很多的开源实现方案涌现出来,如:tcc-transaction、ByteTCC、spring-cloud-rest-tcc。