RabbitMQ 高级:分布式事务简述(一)

前言

分布式事务指事务的操作位于不同的节点上,需要保证事务的 ACID 特性。

例如在下单场景下,订单服务和配送中心如果不在同一个节点上,就涉及分布式事务。

功能描述

订单和配送中心两个服务之间是独立的,现在要把它们变为一个“ 整体 ”:用户下单,订单系统完成订单创建,再远程调用配送中心服务,传递已创建的订单信息过去,配送中心接收到订单详情,再根据该笔订单进行一系列的骑手接单配送等操作,介于两套系统之间,完成一个闭环操作。

问题:数据的一致性如何保证?

往往在线上环境,可能会遇到如下问题,比如配送中心服务出现异常,或者因网络波动,远程调用配送中心接口超时,最终导致订单创建失败,从而引发事务的控制。

由于订单服务和配送中心都是独立的系统,独立的jvm,独立的数据库连接,连接过程中事务的管理只能控制自身的系统,而无法去回滚其他系统

订单系统报错,配送中心无法回滚,配送中心报错,也无法回滚订单服务

事务与事务之间没办法相互控制,根据数据库的ACID 原则,要么全部成功,要么全部失败的特性,显然它们之间破坏了数据库的完整性

影响:最终造成脏数据,数据的不一致性就会出现很多

如何解决跨 jvm的事务呢?

在不同系统之间,我们如何保证数据完整性的解决方案?

在Java中,Spring里提供的对数据库事务支持,只能控制当前 jvm自己级别范围的,也就是无法跨jvm去控制,订单的事务无法控制配送中心的事务,配送中心的事务也无法控制订单的事务。

但是我们可以使用一些方式,来弥补或达成最终事务一致性的解决方案!

分布式事务的方式

在分布式系统中,要实现分布式事务,无外乎那几种结局方案

方式一、两阶段提交(2pc)

——需要数据库产商的支持,Java组建有atomikos等

两阶段提交(Two-phase-commit,2pc) ,通过引入协调者(coordinator)来协调参与者的行为,并最终决定这些参与者是否要真正执行事务

一、准备阶段

协调者询问参与者事务是否执行成功,参与者发回事务执行结果

二、提交阶段

如果事务在每个参与者上都执行成功,事务协调者发送通知让参与者提交事务,否则,协调者发送通知让参与者回滚事务。

需要注意的是,在准备阶段,参与者只是执行了事务,但还未提交 !!   只有在提交阶段接收到协调者发来的通知后,才进行提交或者回滚

2pc存在问题

同步阻塞:所有事务参与者在等待其它参与者响应时,都处于同步阻塞状态,无法进行其它操作

单点问题:协调者在2pc中起到非常大的作用,发生故障将会造成很大影响,特别是在阶段二发生故障,所有参与者会一直等待状态,无法完成其它操作

数据不一致:在阶段二,如果协调者只发送了部分commit消息,此时网络发生异常,那么只有部分参与者接收到commit消息,也就是说只有部分参与者提交了事务,使得系统数据不一致

太过保守:任意一个节点失败就会导致整个事务失败,没有完善的容错机制

方式二、 补偿事务(TCC)  严选、阿里、蚂蚁金服

Tcc 其实就是采用的补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作,它分为三个阶段

  1.  try阶段:主要是对业务系统做检测及预留资源
  2.  confirm阶段:主要是对业务系统做确认提交,try阶段执行成功并开始执行confirm阶段时,默认-- confirm阶段是不会错误的,即只要try成功,confirm一定成功
  3.  cancel阶段:主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放


举例说明

张三要向李四转账,思路大概是:我们有一个本地方法,里面依次调用
   ①. 首先在try阶段,要先调用远程接口把两个人的钱冻结起
   ②. 在confirm阶段,执行远程调用的转账的操作,转账成功进行解冻
   ③. 第二步执行成功,则转账成功,如失败,则调用远程冻结接口对应的解冻方法(cancel)

优点:跟2pc相比,实现及流程相对简单些,但数据的一致性比2pc要差一些

缺点:第2、3步都有可能失败,Tcc属于应用层的补偿方式,需程序员在实现时写很多补偿代码

方式三、本地消息表(异步确保)

比如:支付宝、微信支付主动查询支付状态,对帐单的形式

本地消息表与业务数据表处于同一个数据库中,这样就能利用本地事务来保证在对这两个表的操作满足事务特性,并且使用了消息队列来保证最终一致性

操作流程

  1. 在分布式事务操作的一方,完成写业务数据的操作之后,向本地消息表发送一个消息,本地事务能保证这个消息一定会被写入本地消息表中
  2. 之后将本地消息表中的消息转发到Kafka等消息队列中,如果转发成功,则将消息从本地消息表中删除,否则继续重新转发
  3. 在分布式事务操作的另一方从消息队列中读取一个消息,并执行消息中的操作

优点:一种非常经典的实现,避免了分布式事务,实现了最终一致性

缺点:消息表会耦合到业务系统中,如何没有封装好的解决方案,会有很多杂活需要处理

方式四、MQ事务消息

异步场景,通用型较强,拓展性较高

有些第三方的MQ是支持事务消息的,比如RocketMQ,他们支持事务消息的方式也是类似于采用的二阶段提交,但是市面上一些主流的MQ都是不支持事务消息的,比如Kafka

以阿里的RabbitMQ中间件为例,思路大致为

  • 第一阶段prepared消息,会拿到消息的地址,第二阶段执行本地事务,第三阶段通过第一阶段拿到的地址去访问消息,并修改消息状态
  • 也就是说在业务方法内要想消息队列提交两次请求,一次发送消息和一次确认消息,如果确认消息发送失败了,RabbitMQ会定期扫描消息集群中的事务消息,这时候发现了prepared消息,它会向消息发送者确认,所以生产方需要实现一个check接口,RabbitMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息,这样就保证了消息发送与本地事务同时成功或同时失效

优点:实现了最终一致性,不需要依赖本地数据库事务

缺点:实现难度大,主流MQ不支持,RocketMQ事务消息部分代码也未开源

总结

在本文我们总结并对比了几种分布式解决方案的优缺点,分布式事务本身是一个技术难题,是没有一种完美的方案应对所有场景的,具体还是要根据业务场景去抉择,阿里RocketMQ去实现的分布式事务,现在也有除了很多分布式事务的协调器,比如LCN等


 

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值