一万四千字分布式事务原理解析,全部掌握你还怕面试被问? (4)

另外有一种类似 TCC 的事务解决方案,借助事务状态表来实现。假设要在一个分布式事务中实现调用 repo-service 扣减库存、调用 order-service 生成订单两个过程。在这种方案中,协调者 shopping-service 维护一张如下的事务状态表:

一万四千字分布式事务原理解析,全部掌握你还怕面试被问?

初始状态为 1,每成功调用一个服务则更新一次状态,最后所有的服务调用成功,状态更新到 3。

有了这张表,就可以启动一个后台任务,扫描这张表中事务的状态,如果一个分布式事务一直(设置一个事务周期阈值)未到状态 3,说明这条事务没有成功执行,于是可以重新调用 repo-service 扣减库存、调用 order-service 生成订单。直至所有的调用成功,事务状态到 3。

如果多次重试仍未使得状态到 3,可以将事务状态置为 error,通过人工介入进行干预。

由于存在服务的调用重试,因此每个服务的接口要根据全局的分布式事务 ID 做幂等,原理同 2.4 节的幂等性实现。

2.7 基于消息中间件的最终一致性事务方案

无论是 2PC & 3PC 还是 TCC、事务状态表,基本都遵守 XA 协议的思想,即这些方案本质上都是事务协调者协调各个事务参与者的本地事务的进度,使所有本地事务共同提交或回滚,最终达成一种全局的 ACID 特性。在协调的过程中,协调者需要收集各个本地事务的当前状态,并根据这些状态发出下一阶段的操作指令。

但是这些全局事务方案由于操作繁琐、时间跨度大,或者在全局事务期间会排他地锁住相关资源,使得整个分布式系统的全局事务的并发度不会太高。这很难满足电商等高并发场景对事务吞吐量的要求,因此互联网服务提供商探索出了很多与 XA 协议背道而驰的分布式事务解决方案。其中利用消息中间件实现的最终一致性全局事务就是一个经典方案。

一万四千字分布式事务原理解析,全部掌握你还怕面试被问?

为了表现出这种方案的精髓,我将使用如下的电商系统微服务结构来进行描述:

一万四千字分布式事务原理解析,全部掌握你还怕面试被问?

在这个模型中,用户不再是请求整合后的 shopping-service 进行下单,而是直接请求 order-service 下单,order-service 一方面添加订单记录,另一方面会调用 repo-service 扣减库存。

这种基于消息中间件的最终一致性事务方案常常被误解成如下的实现方式:

一万四千字分布式事务原理解析,全部掌握你还怕面试被问?

这种实现方式的流程是:

1)order-service 负责向 MQ server 发送扣减库存消息(repo_deduction_msg);repo-service 订阅 MQ server 中的扣减库存消息,负责消费消息。

2)用户下单后,order-service 先执行插入订单记录的查询语句,后将 repo_deduction_msg 发到消息中间件中,这两个过程放在一个本地事务中进行,一旦“执行插入订单记录的查询语句”失败,导致事务回滚,“将 repo_deduction_msg 发到消息中间件中”就不会发生;同样,一旦“将 repo_deduction_msg 发到消息中间件中”失败,抛出异常,也会导致“执行插入订单记录的查询语句”操作回滚,最终什么也没有发生。

一万四千字分布式事务原理解析,全部掌握你还怕面试被问?

3)repo-service 接收到 repo_deduction_msg 之后,先执行库存扣减查询语句,后向 MQ sever 反馈消息消费完成 ACK,这两个过程放在一个本地事务中进行,一旦“执行库存扣减查询语句”失败,导致事务回滚,“向 MQ sever 反馈消息消费完成 ACK”就不会发生,MQ server 在 Confirm 机制的驱动下会继续向 repo-service 推送该消息,直到整个事务成功提交;同样,一旦“向 MQ sever 反馈消息消费完成 ACK”失败,抛出异常,也对导致“执行库存扣减查询语句”操作回滚,MQ server 在 Confirm 机制的驱动下会继续向 repo-service 推送该消息,直到整个事务成功提交。

一万四千字分布式事务原理解析,全部掌握你还怕面试被问?

这种做法看似很可靠。但没有考虑到网络二将军问题的存在,有如下的缺陷:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwdfjp.html