分布式事务Seata-AT模式解析一

Seata-AT模式的执行流程:
在这里插入图片描述

@GlobalTransactional(name = "fsp-create-order",timeoutMills = 30000,rollbackFor = Exception.class)
public void create(Order order) {
    LOGGER.info("------->交易开始");
    //本地方法
    orderDao.create(order);
   // 远程方法 扣减库存
    storageApi.decrease(order.getProductId(),order.getCount());
    //int i = 1/0;
    //远程方法 扣减账户余额
    LOGGER.info("------->扣减账户开始order中");
    accountApi.decrease(order.getUserId(),order.getMoney());
    LOGGER.info("------->扣减账户结束order中");
    LOGGER.info("------->交易结束");
}
  1. order-sevice执行创建订单时候,方法上使用@GlobalTransactional注解会在启动中通过GlobalTransactionScanner类生成代理对象。
  2. 执行create方法前,代理对象会通过TM向seata-server发送全局事务的begin请求,seata-server收到请求生成XID,插入global_table中记录,返回XID。
  3. 执行orderDao.create(order)方法,会通过RM向seata-server中注册一个事务分支,seata-server记录分支插入branch_table表,返回branchId,然后生成undo_log记录,插入本地数据库,和业务处理一起提交本地事务。
  4. storageApi.decrease(order.getProductId(),order.getCount())执行,会使用seata的SeataLoadBalancerFeignClient发起请求,会把XID放入header中传递。
  5. 后续操作都一样 ,RM向seata-server中注册,然后插入branch_table表,返回branchId,然后生成undo_log记录,插入本地数据库,和业务处理一起提交本地事务。

二阶提交:

  1. 收到 TC 的分支提交请求,把请求放入一个异步任务的队列中,马上返回提交成功的结果给 TC。
  2. 异步任务阶段的分支提交请求将异步和批量地删除相应 UNDO LOG 记录。

二阶回滚:

  1. 收到 TC 的分支回滚请求,开启一个本地事务,执行如下操作。
  2. 通过 XID 和 Branch ID 查找到相应的 UNDO LOG 记录。
  3. 数据校验:拿 UNDO LOG 中的后镜与当前数据进行比较,如果有不同,说明数据被当前全局事务之外的动作做了修改。这种情况,需要根据配置策略来做处理,详细的说明在另外的文档中介绍
  4. 根据 UNDO LOG 中的前镜像和业务 SQL 的相关信息生成并执行回滚的语句:
  5. 提交本地事务。并把本地事务的执行结果(即分支事务回滚的结果)上报给 TC。

我们先来看看Seata-AT模式中,如何向seata-server中发起一个全局事务beign请求,先看到GlobalTransactionScanner如何生成代理对象的。
在这里插入图片描述
这里我们看到SpringBoot的自动装配GlobalTransactionAutoConfiguration类
在这里插入图片描述
配置了GlobalTransactionScanner,我们看看GlobalTransactionScanner类

在这里插入图片描述
我们看到GlobalTransactionScanner继承了AbstractAutoProxyCreator类,这里不详解这个AbstractAutoProxyCreator的作用(遍历Spring管理的类,找到满足你条件的类,进行增强)
在这里插入图片描述
在这里插入图片描述

AbstractAutoProxyCreator重写了wrapIfNecessary方法

  1. 判断这个类方法上是否有@GlobalTransactional注解
  2. 增强的类GlobalTransactionalInterceptor ,所以我们方法上有@GlobalTransactional注解会执行GlobalTransactionalInterceptor 类的invoke方法,所以我们在执行order-service的create方法时,会使用GlobalTransactionalInterceptor进行增强,我们执行create的方法,执行该类的invoke方法,我们去看看

在这里插入图片描述
invoke方法中,获取该方法上注解,我们这里执行handleGlobalTransaction(methodInvocation, globalTransactionalAnnotation)方法
在这里插入图片描述
执行我们的方法前,会先执行transactionalTemplate.execute 方法
在这里插入图片描述
上面会通过XID查看当前是否有GlobalTransaction,如何没有就创建一个新的,重点看
beginTransaction方法
在这里插入图片描述
在这里插入图片描述
都是调用,继续往下追踪
在这里插入图片描述
注意:(这里GlobalBeginRequest类里面有个getTypeCode()方法比较重要,后续seata-server会通过getTypeCode的返回值找到对应的处理器,我们后续在细说)
继续追踪syncCall方法
在这里插入图片描述
这里创建了一个TMClient实例,继续看方法sendMsgWithResponse方法
在这里插入图片描述
继续追踪
在这里插入图片描述
loadBalance方法会去注册中心,拿到seata-server地址(这里不详细说明了)
clientChannelManager.acquireChannel 这里拿到请求地址去请求连接(这里使用的时netty进行连接)
我们继续看重点
在这里插入图片描述
在这里插入图片描述
构造RpcMessage 消息,进行后续发送
在这里插入图片描述
继续追踪
在这里插入图片描述
通过netty的channel.writeAndFlush(rpcMessage) 发送消息,客户端发送消息就完成了,我们现在来看看seata-server如何处理的

seata-server执行:
在这里插入图片描述
启动Server的main方法
在这里插入图片描述
我们看到init方法
在这里插入图片描述
registerProcessor 该方法时注册处理器(我们后续在细看,暂时我们看netty如何启动的)
super.init 进入,继续分析
在这里插入图片描述
我们这里看到 serverBootstrap.start()方法执行
在这里插入图片描述
我们这里看到心跳,编码,解码,还有一个channelHandlers,我们看看这个channelHandlers是什么
在这里插入图片描述
Server类main方法中NettyRemotingServer创建对象默认new了一个ServerHandler对象,我们来看看这个对象
在这里插入图片描述
我们seata-server中处理请求的handler就是这个,前面我们TMClient向channel通道中写入了一条RpcMessage消息,这里我们会读取到,我们看看seata-server读取到消息以后如何处理的
在这里插入图片描述
在这里插入图片描述
我们这里,拿到消息 通过消息的body(这里的body就是前面的GlobalBeginRequest对象)来找到对应的处理器,我们这里回过头来,我们前面说的消息请求是GlobalBeginRequest,我们看看getTypeCode()返回的是什么
在这里插入图片描述
我们看到getTypeCode返回的是一个枚举MessageType.TYPE_GLOBAL_BEGIN,前面说的registerProcessor方法注册处理器,我们现在来看看
在这里插入图片描述
这里把对应的类型的处理器进行注册,我们看看MessageType.TYPE_GLOBAL_BEGIN是ServerOnRequestProcessor处理器,所以我们的消息会执行ServerOnRequestProcessor.process方法进行处理消息,我们去看看

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
继续追踪
在这里插入图片描述
执行GlobalBeginRequest的handle方法
在这里插入图片描述
这里执行doGlobalBegin方法
在这里插入图片描述
这里执行core.begin方法,把返回的XID设置到response对象中,我们继续看core.begin方法执行

在这里插入图片描述
创建GlobalSession执行begin()方法(GlobalSession.createGlobalSession()方法中会生成XID,XID生成规则是IP地址+ 端口号+ UUID生成的transactionId)
在这里插入图片描述
执行lifecycleListener.onBegin方法
在这里插入图片描述执行addGlobalSession方法在这里插入图片描述
执行writeSession方法
在这里插入图片描述
执行transactionStoreManager.writeSession(logOperation, sessionStorable)方法(这里是根据配置规则来,我配置的DB,还可以配置File,和Redis,配置Redis就插入Redis,配置DB就插入DB)

在这里插入图片描述
我们这里是LogOperation.GLOBAL_ADD类型,所以执行insertGlobalTransactionDO方法

在这里插入图片描述
这里会根据配置的DB选择不同的sql语句去插入,我配置的是Mysql
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
拿到sql语句去执行插入操作,这里seata-server就完成了global_table表的操作,插入完成返回XID
客户端拿到XID通过ThreadLocal绑定到本地线程,TM注册的一个过程就完成了。(太长了 后续的操作我们解析二在来说明)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值