最新 Seata 集成了RocketMQ事务消息,Seata 越来越 牛X 了! yyds !

文摘   科技   2024-10-22 20:56   湖北  
FSAC未来超级架构师

架构师总动员
实现架构转型,再无中年危机


尼恩说在前面

在40岁老架构师 尼恩的读者交流群(50+)中,最近有小伙伴拿到了一线互联网企业如阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格,遇到很多很重要的面试题:
Seata是如何解决分布式事务问题的?Seata 的使用场景有哪些?
Seata 如何实现 事务的隔离性?
如何实现 强弱一致性 结合的分布式事务?
所以,尼恩给大家做一下系统化、体系化的梳理,使得大家内力猛增,可以充分展示一下大家雄厚的 “技术肌肉”,让面试官爱到 “不能自已、口水直流”,然后实现”offer直提”。
之前给大家梳理过 Seata 如何实现 事务的隔离性, 具体参见下文:
阿里面试:Seata 如何实现 RC ?保证事务的隔离性?
这里 借助 Seata 集成 RocketMQ 事务消息的 新功能,介绍一下一个新遇到的面试题:如果如何实现 强弱一致性 结合的分布式事务?
当然, 强弱一致性 结合的分布式事务的面试题以及参考答案,也会收入咱们的 《尼恩Java面试宝典PDF》V140版本,供后面的小伙伴参考,提升大家的 3高 架构、设计、开发水平。
《尼恩 架构笔记》《尼恩高并发三部曲》《尼恩Java面试宝典》的PDF,请到文末公号【技术自由圈】获取

本文目录

尼恩说在前面

一:回顾Seat AT 模式 

 - AT模式角色如下

 - 分支事务 处理逻辑如下

 - AT模式第一阶段  Prepare 阶段

    -这也是Seata和XA事务的不同之处:

 - AT模式第二阶段

    -场景一:提交,全局提交
    -场景二:回滚,全局回滚

 - AT模式相对于XA模式的优势

 - 秒杀实操的AT分布式事务架构

 - Seat AT 模式的不足

二:回顾Seat TCC 模式 

 - Seata TCC基本原理

 - Seata TCC模式的流程图

 - Seata TCC 事务的3个操作

 - TCC 模式第一阶段  try 阶段

 - TCC 模式第二阶段  

 - TCC版本的秒杀的分布式事务架构

 - Seata TCC 事务的弱点

三:新功能  Seata + Rocketmq 事务消息实现 强弱结合型事务

 - Seata  + Rocketmq 事务消息 结合

 - Seata  + Rocketmq 事务消息 结合的使用场景

- 说在最后:有问题找老架构取经

一:回顾Seat AT 模式

Seata AT模式是基于XA事务演进而来的一个分布式事务中间件,
XA是一个基于数据库实现的分布式事务协议,本质上和两阶段提交一样,需要数据库支持,Mysql5.6以上版本支持XA协议,其他数据库如Oracle,DB2也实现了XA接口

AT模式角色如下

1、Transaction Coordinator (TC):事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚
2、Transaction Manager ™:
控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议
3、Resource Manager (RM):
控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚
来自官方的图:

分支事务 处理逻辑如下

Branch就是指的分布式事务中每个独立的本地局部事务

AT模式第一阶段 Prepare 阶段

看看尼恩给大家画的 Prepare 阶段 简单的图
Seata 的 JDBC 数据源代理通过对业务 SQL 的解析,把业务数据在更新前后的数据镜像组织成回滚日志,利用 本地事务 的 ACID 特性,将业务数据的更新和回滚日志的写入在同一个 本地事务 中提交。
这样,可以保证:任何提交的业务数据的更新一定有相应的回滚日志存在
基于这样的机制,分支的本地事务便可以在全局事务的第一阶段提交,并马上释放本地事务锁定的资源
这也是Seata和XA事务的不同之处:
经典的2PC两阶段提交(XA)往往对资源的锁定需要持续到第二阶段实际的提交或者回滚操作
AT模式,可以在第一阶段释放对资源的锁定,降低了锁范围
谁的功劳:回滚日志

AT模式第二阶段

看看尼恩给大家画的 commit/rollback 阶段 简单的图
两种情况
场景一:提交,全局提交
如果决议是全局提交,此时分支事务此时已经完成提交,不需要同步协调处理(只需要异步清理回滚日志),Phase2 可以非常快速地完成
场景二:回滚,全局回滚
如果决议是全局回滚,RM 收到协调器发来的回滚请求,通过 XID 和 Branch ID 找到相应的回滚日志记录,通过回滚记录生成反向的更新 SQL 并执行,以完成分支的回滚

AT模式相对于XA模式的优势

提高效率,即使第二阶段发生异常需要回滚,只需找对undolog中对应数据并反解析成sql来达到回滚目的
同时Seata无入侵,通过代理数据源将业务sql的执行解析成undolog来与业务数据的更新同时入库,达到了对业务无侵入的效果

秒杀实操的AT分布式事务架构

Seat AT 模式的不足

AT模式的依赖的还是依赖本地数据源的事务控制能力,完成分支事务的管理。
AT模式 采用的是wal的思想,提交事务的时候同时记录undolog,如果全局事务成功,则删除undolog,如果失败,则使用undolog的数据回滚分支事务,最后删除undolog。
Seat AT 模式的性能低,而且非常低。

二:回顾Seat TCC 模式

Seat TCC 模式 的性能会高些, 脱离了本地数据库事务, 需要业务控制 两个阶段操作的 原子性。

Seata TCC基本原理

TCC模式的特点是不再依赖于undolog,

Seata TCC模式的流程图

TCC模式还是采用2阶段提交的方式:
  • 第一阶段使用prepare尝试事务提交,
  • 第二阶段使用commit或者rollback让事务提交或者回滚。
引用网上一张TCC原理的参考图片
来自官方的图:

Seata TCC 事务的3个操作

TCC 将事务提交分为 Try - Confirm - Cancel 3个操作。
其和两阶段提交有点类似,Try为第一阶段,Confirm - Cancel为第二阶段,是一种应用层面侵入业务的两阶段提交。
操作方法含义
Try
预留业务资源/数据效验
Confirm
确认执行业务操作,实际提交数据,不做任何业务检查,try成功,confirm必定成功,需保证幂等
Cancel
取消执行业务操作,实际回滚数据,需保证幂等
其核心在于将业务分为两个操作步骤完成。不依赖 RM 对分布式事务的支持,而是通过对业务逻辑的分解来实现分布式事务。
下面还以银行转账例子来说明
假设用户user表中有两个字段:可用余额(available_money)、冻结余额(frozen_money)
  • A扣钱对应服务A(ServiceA)
  • B加钱对应服务B(ServiceB)
  • 转账订单服务(OrderService)
  • 业务转账方法服务(BusinessService)
ServiceA,ServiceB,OrderService都需分别实现try(),confirm(),cancle()方法,方法对应业务逻辑如下

ServiceAServiceBOrderService
try()
校验余额(并发控制) 冻结余额+1000 余额-1000
冻结余额+1000
创建转账订单,状态待转账
confirm()
冻结余额-1000
余额+1000 冻结余额-1000
状态变为转账成功
cancle()
冻结余额-1000 余额+1000
冻结余额-1000
状态变为转账失败
其中业务调用方BusinessService中就需要调用
  • ServiceA.try()
  • ServiceB.try()
  • OrderService.try()
1、当所有try()方法均执行成功时,对全局事物进行提交,即由事物管理器调用每个微服务的confirm()方法 2、 当任意一个方法try()失败(预留资源不足,抑或网络异常,代码异常等任何异常),由事物管理器调用每个微服务的cancle()方法对全局事务进行回滚

TCC 模式第一阶段 try 阶段

看看尼恩给大家画的 try 阶段 简单的图
try 阶段 没有本地事务了。需要业务维护 本地操作的 原子性,一致性。

TCC 模式第二阶段 

看看尼恩给大家画的第二阶段 简单的图
TCC 模式 第二 阶段 也没有本地的undo 日志支持。需要业务维护 完成 本地 的提交操作, 或者 回滚操作。
而 AT模式的话, 本地 的提交操作, 或者 回滚操作 是 proxy 代理结合 undo 日志 完成的, 是业务无感知的, 无入侵的。

TCC版本的秒杀的分布式事务架构

Seata TCC 事务的弱点

Seata TCC 事务脱离了对数据库的强依赖, 很明确来说,性能会高些。
但是 Seata TCC 事务的弱点不少,大致如下:
  • 需要自定义 提交操作 + 回滚操作, 存在业务入侵。
  • 需要处理 悬挂 问题
  • 需要处理 空回滚问题

三:新功能 Seata + Rocketmq 事务消息实现 强弱结合型事务

首先看看 RocketMQ 的事务消息, 能够保证本地操作 + 消息发送的原子性。
具体来说, 主要是保证了本地方法执行和消息发送在一个分布式事务中,要不全部成功,要不全部失败。
见下图:
RocketMQ 通过发送 half 消息来实现,下面详细说明一下:
  1. 消息发送方 向 Broker 发送一条 half 消息;
  2. half 消息发送成功后,消息发送方 执行本地事务;
  3. 如果 消息发送方 执行本地事务成功,则向 Broker 发送 commit 请求,否则发送 rollback 请求;
  4. 如果 Broker 收到的是 rollback 请求,则删除保存的 half 消息;
  5. 如果 Broker 收到的是 commit 请求,则把 half 消息投递到 真实 队列, 等待消费服务来拉取,然后删除保存的 half 消息;
  6. 如果 Broker 没有收到 rollback/commit 请求,则会发送请求到 Producer 查询本地事务状态,然后根据 Producer 返回的本地状态做 commit/rollback 相关处理。

Seata + Rocketmq 事务消息 结合

Seata + Rocketmq 事务消息 结合的目标:
  • 一 是保证 分布式事务 + 消息 发送的原子性。
  • 二 是 再通过mq的重试机制,去保证订阅者的最终一致性,
Seata 的改进主要在 prepare 阶段。
Seata 提供了一个 SeataMQProducer 类,把 RocketMQ 中 TransactionListener 的方法加入到全局事务。
见下面代码:
SeataMQProducer(final String namespace, final String producerGroup, RPCHook rpcHook) {
super(namespace, producerGroup, rpcHook);
this.transactionListener = new TransactionListener() {
@Override
public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
return LocalTransactionState.UNKNOW;
}

@Override
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
String xid = msg.getProperty(PROPERTY_SEATA_XID);
if (StringUtils.isBlank(xid)) {
LOGGER.error("msg has no xid, msgTransactionId: {}, msg will be rollback", msg.getTransactionId());
return LocalTransactionState.ROLLBACK_MESSAGE;
}
GlobalStatus globalStatus = DefaultResourceManager.get().getGlobalStatus(SeataMQProducerFactory.ROCKET_BRANCH_TYPE, xid);
if (COMMIT_STATUSES.contains(globalStatus)) {
return LocalTransactionState.COMMIT_MESSAGE;
} else if (ROLLBACK_STATUSES.contains(globalStatus) || GlobalStatus.isOnePhaseTimeout(globalStatus)) {
return LocalTransactionState.ROLLBACK_MESSAGE;
} else if (GlobalStatus.Finished.equals(globalStatus)) {
LOGGER.error("global transaction finished, msg will be rollback, xid: {}", xid);
return LocalTransactionState.ROLLBACK_MESSAGE;
}
return LocalTransactionState.UNKNOW;
}
};
在 prepare 阶段,SeataMQProducer 向 RocketMQ Broker 发送 half 消息,执行本地事务,如果执行成功,则强行把 LocalTransactionState 改回 UNKNOW,等待 TC 发送指令,决定是 commit 或 rollback。
如果执行失败,返回 ROLLBACK_MESSAGE,TC 下发指令,回滚全局事务。

Seata + Rocketmq 事务消息 结合的使用场景

如果 MQ Broker 没有收到 commit/rollback 消息,则会回查 Producer 本地事务状态,也就是上面代码中的 checkLocalTransaction。
checkLocalTransaction 检查 全局事务状态,使用 XID 去查询 全局事务,去决定 half 消息 是 抛弃还是 投递 。
集成 RocketMQ 之后,Seata 的分布式事务调用流程, 下面以 订单服务、库存服务两个服务为例:
Apache Seata 引入 RocketMQ 后,支持的分布式事务场景更加丰富,使得 Seata 可以用于 强一致性 + 弱一致性 结合的场景。

说在最后:有问题找老架构取经

以上关于分布式事务的方案和场景,如果大家能对答如流,如数家珍,基本上 面试官会被你 震惊到、吸引到。
最终,让面试官爱到 “不能自已、口水直流”。offer, 也就来了。
在面试之前,建议大家系统化的刷一波 5000页《尼恩Java面试宝典PDF》,里边有大量的大厂真题、面试难题、架构难题。很多小伙伴刷完后, 吊打面试官, 大厂横着走。
在刷题过程中,如果有啥问题,大家可以来 找 40岁老架构师尼恩交流。
另外,如果没有面试机会,可以找尼恩来改简历、做帮扶。
遇到职业难题,找老架构取经, 可以省去太多的折腾,省去太多的弯路。
尼恩指导了大量的小伙伴上岸,前段时间,刚指导中厂大龄34岁,被裁8月收一大厂offer, 年薪65W,转架构后逆天改命!
狠狠卷,实现 “offer自由” 很容易的, 前段时间一个武汉的跟着尼恩卷了2年的小伙伴, 在极度严寒/痛苦被裁的环境下, offer拿到手软, 实现真正的 “offer自由” 。

 空窗1年/空窗2年,如何通过一份绝世好简历,  起死回生  ? 

空窗8月:中厂大龄34岁,被裁8月收一大厂offer, 年薪65W,转架构后逆天改命!


空窗2年:42岁被裁2年,天快塌了,急救1个月,拿到开发经理offer,起死回生


空窗半年:35岁被裁6个月, 职业绝望,转架构急救上岸,DDD和3高项目太重要了

空窗1.5年:失业15个月,学习40天拿offer, 绝境翻盘,如何实现?


 100W 年薪  大逆袭,  如何实现  ? 


100W案例,100W年薪的底层逻辑是什么? 如何实现年薪百万? 如何远离  中年危机?

100W案例240岁小伙被裁6个月,猛卷3月拿100W年薪 ,秘诀:首席架构/总架构

环境太糟,如何升 P8级,年入100W?

如何  评价一份绝世好简历, 实现逆天改命,包含AI、大数据、golang、Java  等 


职业救助站

实现职业转型,极速上岸


关注职业救助站公众号,获取每天职业干货
助您实现职业转型、职业升级、极速上岸
---------------------------------

技术自由圈

实现架构转型,再无中年危机


关注技术自由圈公众号,获取每天技术千货
一起成为牛逼的未来超级架构师

几十篇架构笔记、5000页面试宝典、20个技术圣经
请加尼恩个人微信 免费拿走

暗号,请在 公众号后台 发送消息:领电子书

如有收获,请点击底部的"在看"和"",谢谢

技术自由圈
疯狂创客圈(技术自由架构圈):一个 技术狂人、技术大神、高性能 发烧友 圈子。圈内一大波顶级高手、架构师、发烧友已经实现技术自由;另外一大波卷王,正在狠狠卷,奔向技术自由
 最新文章