Seata 1.5.2 源码学习(Server端)

Seata 包括 Server端和Client端。Seata中有三种角色:TC、TM、RM,其中,Server端就是TC,TM和RM属Client端。Client端的源码学习上一篇已讲过,详见《Seata 1.5.2 源码学习》,今天来学习Server端的源码。

源码下载地址:https://github.com/seata/seata

启动类 ServerApplication 没什么好说的,重点是ServerRunner

Seata 1.5.2 源码学习(Server端)_第1张图片

ServerRunner 是一个 CommandLineRunner 实例,因此在Spring Boot启动完成后会回调其run()方法。而在ServerRunner的run()方法中调用了Server.start()方法。

Seata 1.5.2 源码学习(Server端)_第2张图片

在Server#start()方法中,初始化了包括id生成器在内的很多组件,我们先不管这些,重点关注以下几行代码:

NettyRemotingServer nettyRemotingServer = new NettyRemotingServer(workingThreads);
DefaultCoordinator coordinator = DefaultCoordinator.getInstance(nettyRemotingServer);
coordinator.init();
nettyRemotingServer.setHandler(coordinator);

 DefaultCoordinator是一个单例Bean,在整个应用中只有一个DefaultCoordinator实例

Seata 1.5.2 源码学习(Server端)_第3张图片

Seata 1.5.2 源码学习(Server端)_第4张图片

Seata 1.5.2 源码学习(Server端)_第5张图片

DefaultCoordinator 实现了 TransactionMessageHandler 

NettyRemotingServer#setHandler()设置的正是TransactionMessageHandler

Seata 1.5.2 源码学习(Server端)_第6张图片DefaultCoordinator#onRequest()

Seata 1.5.2 源码学习(Server端)_第7张图片

 重点是这三行:

AbstractTransactionRequestToTC transactionRequest = (AbstractTransactionRequestToTC) request;
transactionRequest.setTCInboundHandler(this);
transactionRequest.handle(context);

 DefaultCoordinator实现了TCInboundHandler接口,所以它不仅是一个TransactionMessageHandler,还是一个TCInboundHandler

​这里transactionRequest.setTCInboundHandler(this),就是指定AbstractTransactionRequestToTC中的TCInboundHandler设置为DefaultCoordinator

Seata 1.5.2 源码学习(Server端)_第8张图片

Seata 1.5.2 源码学习(Server端)_第9张图片

 AbstractTransactionRequest#handle()

不同的请求分发给对应的处理器去处理

Seata 1.5.2 源码学习(Server端)_第10张图片

开启全局事务

Seata 1.5.2 源码学习(Server端)_第11张图片

开启事务直接调用子类DefaultCoordinator#doGlobalBegin(),同时放在一个处理模板中执行

Seata 1.5.2 源码学习(Server端)_第12张图片

在doGlobalBean()中调用DefaultCore#begin()并返回全局事务ID(xid)

Seata 1.5.2 源码学习(Server端)_第13张图片

 new GlobalSession()

Seata 1.5.2 源码学习(Server端)_第14张图片

添加一个SessionManager作为Session的监听器

Seata 1.5.2 源码学习(Server端)_第15张图片Core 

Seata 1.5.2 源码学习(Server端)_第16张图片

总结一下,开启事务:

  1. 创建一个GlobalSession
  2. 给GlobalSession添加一个监听器SessionManager
  3. session.begin() 

Seata 1.5.2 源码学习(Server端)_第17张图片

开启事务,创建一个全局事务,如果是seata.store.mode=db的话,向global_table表插入一条记录

分支事务注册 

Seata 1.5.2 源码学习(Server端)_第18张图片

Seata 1.5.2 源码学习(Server端)_第19张图片

DefaultCore#branchRegister()

Seata 1.5.2 源码学习(Server端)_第20张图片

Seata 1.5.2 源码学习(Server端)_第21张图片 Seata 1.5.2 源码学习(Server端)_第22张图片

如果是AT模式,这里调用的就是ATCore#branchSessionLock()

ATCore#branchSessionLock()检查是否拿到锁了

Seata 1.5.2 源码学习(Server端)_第23张图片

Seata 1.5.2 源码学习(Server端)_第24张图片

Seata 1.5.2 源码学习(Server端)_第25张图片

Seata 1.5.2 源码学习(Server端)_第26张图片

Seata 1.5.2 源码学习(Server端)_第27张图片

 Seata 1.5.2 源码学习(Server端)_第28张图片

具体每种锁的实现就不一一看了,挑其中一个看下,就RedisLocker吧

Seata 1.5.2 源码学习(Server端)_第29张图片

总之,分支注册的时候需要检查锁,拿到本次事务中所涉及的所有需要加锁的行的锁才能注册成功

所有行都加锁成功,分支注册才算成功,才会返回true

再回到AbstractCore#branchRegister(),整个方法是放在SessionHolder#lockAndExecute()中执行的

Seata 1.5.2 源码学习(Server端)_第30张图片

总结一下,分支注册: 

  1. 创建一个BranchSession
  2. 加锁,获取所有行的锁
  3. 将BranchSession加到GlobalSession中
  4. 返回branchId

Seata 1.5.2 源码学习(Server端)_第31张图片

分支注册,创建BranchSession,获取全局锁成功后将branchSession加入globalSession

提交全局事务

Seata 1.5.2 源码学习(Server端)_第32张图片Seata 1.5.2 源码学习(Server端)_第33张图片

首先判断全局事务状态是否为begin,如果不是则不应该提交。如果是,则将事务active置为false,释放全局锁,判断是否可以异步提交。分支类型是AT的都可以异步提交,因此AT模式,默认是异步提交。如果不能异步提交,则采取同步提交。

同步提交 

Seata 1.5.2 源码学习(Server端)_第34张图片

Seata 1.5.2 源码学习(Server端)_第35张图片

Seata 1.5.2 源码学习(Server端)_第36张图片

遍历所有已注册的分支事务,向分支发送同步请求,告诉它全局事务开始提交了,不出意外的情况下返回分支状态是二阶段提交成功。当所有分支都提交成功,则返回true,于是全局事务提交成功,返回全局事务状态为已提交。如果有分支提交失败,则返回false,全局事务提交失败,返回全局状态为提交失败。如果抛异常了,则会有定时任务稍后重试提交。

异步提交 

Seata 1.5.2 源码学习(Server端)_第37张图片

异步提交只是将全局状态置为异步提交中,剩下的事情交给定时任务去执行

启动的时候调用了DefaultCoordinator#init()方法,启动定时任务

Seata 1.5.2 源码学习(Server端)_第38张图片

Seata 1.5.2 源码学习(Server端)_第39张图片

每次,定时任务执行前,要先获取一把分布式锁,这个锁是io.seata.core.store.DistributedLocker,不是分支注册时的那把锁io.seata.core.lock.Locker

Seata 1.5.2 源码学习(Server端)_第40张图片

Seata 1.5.2 源码学习(Server端)_第41张图片

异步提交首先将全局状态设置为AsyncCommitting,返回返回全局状态Committed。后台有定时任务扫描,找到所有状态为AsyncCommitting的全局事务,循环遍历。对于每个全局事务提交,调用DefaultCore#doGlobalCommit(),遍历所有已注册的分支事务,向分支事务发请求,通知其提交事务,分支事务返回二阶段提交成功,表示该分支事务提交成功,当所有分支事务都二阶段提交成功,则全局事务提交成功。

Seata 1.5.2 源码学习(Server端)_第42张图片

回滚全局事务 

 Seata 1.5.2 源码学习(Server端)_第43张图片

Seata 1.5.2 源码学习(Server端)_第44张图片

Seata 1.5.2 源码学习(Server端)_第45张图片

Seata 1.5.2 源码学习(Server端)_第46张图片

Seata 1.5.2 源码学习(Server端)_第47张图片

Seata 1.5.2 源码学习(Server端)_第48张图片

回滚,首先检查全局事务状态是否为Begin,不是的话直接结束。遍历当前全局事务中已注册的分支事务,依次给每个分支发请求,告诉分支事务需要回滚。如果所有分支返回回滚成功,则全局回滚成功。如果有分支回滚失败且不重试,则直接直接结束。如果失败且可重试,或者执行过程中抛异常,则稍后会有定时任务重试回滚操作。

Seata 1.5.2 源码学习(Server端)_第49张图片

分支报告

RM向TC报告分支事务状态 

Seata 1.5.2 源码学习(Server端)_第50张图片

Seata 1.5.2 源码学习(Server端)_第51张图片

只是更新一下分支状态及相关数据 

查询全局事务状态

Seata 1.5.2 源码学习(Server端)_第52张图片

查询全局锁

Seata 1.5.2 源码学习(Server端)_第53张图片Seata 1.5.2 源码学习(Server端)_第54张图片

Seata 1.5.2 源码学习(Server端)_第55张图片

 挑RedisLocker看一下吧

Seata 1.5.2 源码学习(Server端)_第56张图片

关于Seata Server 的源码学习就先到这里,欢迎交流,多谢点赞 (^_^) 

原文:Seata Server 1.5.2 源码学习 - 废物大师兄 - 博客园

你可能感兴趣的:(Seata,java,分布式,数据库)