Seata Server 1.5.2 源码学习
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
ServerRunner 是一个 CommandLineRunner 实例,因此在Spring Boot启动完成后会回调其run()方法。而在ServerRunner的run()方法中调用了Server.start()方法。
在Server#start()方法中,初始化了包括id生成器在内的很多组件,我们先不管这些,重点关注以下几行代码:
NettyRemotingServer nettyRemotingServer = new NettyRemotingServer(workingThreads);
DefaultCoordinator coordinator = DefaultCoordinator.getInstance(nettyRemotingServer);
coordinator.init();
nettyRemotingServer.setHandler(coordinator);
DefaultCoordinator是一个单例Bean,在整个应用中只有一个DefaultCoordinator实例
DefaultCoordinator 实现了 TransactionMessageHandler
NettyRemotingServer#setHandler()设置的正是TransactionMessageHandler
DefaultCoordinator#onRequest()
重点是这三行:
AbstractTransactionRequestToTC transactionRequest = (AbstractTransactionRequestToTC) request;
transactionRequest.setTCInboundHandler(this);
transactionRequest.handle(context);
DefaultCoordinator实现了TCInboundHandler接口,所以它不仅是一个TransactionMessageHandler,还是一个TCInboundHandler
这里transactionRequest.setTCInboundHandler(this),就是指定AbstractTransactionRequestToTC中的TCInboundHandler设置为DefaultCoordinator
AbstractTransactionRequest#handle()
不同的请求分发给对应的处理器去处理
现在请求和对应的处理器都有了,下面具体看一下每种请求都是如何被处理的
1. 开启全局事务
开启事务直接调用子类DefaultCoordinator#doGlobalBegin(),同时放在一个处理模板中执行
在doGlobalBean()中调用DefaultCore#begin()并返回全局事务ID(xid)
new GlobalSession()
添加一个SessionManager作为Session的监听器
Core
总结一下,开启事务:
- 创建一个GlobalSession
- 给GlobalSession添加一个监听器SessionManager
- session.begin()
开启事务,创建一个全局事务,如果是seata.store.mode=db的话,向global_table表插入一条记录
2. 分支事务注册
DefaultCore#branchRegister()
如果是AT模式,这里调用的就是ATCore#branchSessionLock()
ATCore#branchSessionLock()检查是否拿到锁了
具体每种锁的实现就不往下看了,挑其中一个看下,就RedisLocker吧
总之,分支注册的时候需要检查锁,拿到本次事务中所涉及的所有需要加锁的行的锁才能注册成功
所有行都加锁成功,分支注册才算成功,才会返回true
再回到AbstractCore#branchRegister(),整个方法是放在SessionHolder#lockAndExecute()中执行的
总结一下,分支注册:
- 创建一个BranchSession
- 加锁,获取所有行的锁
- 将BranchSession加到GlobalSession中
- 返回branchId
分支注册,创建BranchSession,获取全局锁成功后将branchSession加入globalSession
3. 提交全局事务
首先判断全局事务状态是否为begin,如果不是则不应该提交。如果是,则将事务active置为false,释放全局锁,判断是否可以异步提交。分支类型是AT的都可以异步提交,因此AT模式,默认是异步提交。如果不能异步提交,则采取同步提交。
3.1. 同步提交
遍历所有已注册的分支事务,向分支发送同步请求,告诉它全局事务开始提交了,不出意外的情况下返回分支状态是二阶段提交成功。当所有分支都提交成功,则返回true,于是全局事务提交成功,返回全局事务状态为已提交。如果有分支提交失败,则返回false,全局事务提交失败,返回全局状态为提交失败。如果抛异常了,则会有定时任务稍后重试提交。
3.2. 异步提交
异步提交只是将全局状态置为异步提交中,剩下的事情交给定时任务去执行
启动的时候调用了DefaultCoordinator#init()方法,启动定时任务
每次,定时任务执行前,要先获取一把分布式锁,这个锁是io.seata.core.store.DistributedLocker,不是分支注册时的那把锁io.seata.core.lock.Locker
异步提交首先将全局状态设置为AsyncCommitting,返回返回全局状态Committed。后台有定时任务扫描,找到所有状态为AsyncCommitting的全局事务,循环遍历。对于每个全局事务提交,调用DefaultCore#doGlobalCommit(),遍历所有已注册的分支事务,向分支事务发请求,通知其提交事务,分支事务返回二阶段提交成功,表示该分支事务提交成功,当所有分支事务都二阶段提交成功,则全局事务提交成功。
4. 回滚全局事务
回滚,首先检查全局事务状态是否为Begin,不是的话直接结束。遍历当前全局事务中已注册的分支事务,依次给每个分支发请求,告诉分支事务需要回滚。如果所有分支返回回滚成功,则全局回滚成功。如果有分支回滚失败且不重试,则直接直接结束。如果失败且可重试,或者执行过程中抛异常,则稍后会有定时任务重试回滚操作。
5. 分支上报
RM向TC报告分支事务状态
只是更新一下分支状态及相关数据
6. 查询全局事务状态
7. 查询全局锁
挑RedisLocker看一下吧
关于Seata Server 的源码学习就先到这里,欢迎交流,多谢点赞 (^_^)
Seata Server 1.5.2 源码学习的更多相关文章
- Seata 1.5.2 源码学习
文章有点长,我决定用半个小时来给您分享~ 基于Seata 1.5.2,项目中用 seata-spring-boot-starter 1. SeataDataSourceAutoConfiguratio ...
- MVC系列——MVC源码学习:打造自己的MVC框架(四:了解神奇的视图引擎)
前言:通过之前的三篇介绍,我们基本上完成了从请求发出到路由匹配.再到控制器的激活,再到Action的执行这些个过程.今天还是趁热打铁,将我们的View也来完善下,也让整个系列相对完整,博主不希望烂尾. ...
- [tomcat7源码学习]初始化之catalina.home和catalina.base(转)
我们在代码中为了获取某个配置文件路径下的文件经常会这么写 String tomcatPath = System.getProperty("catalina.home") + &qu ...
- 源码学习之ASP.NET MVC Application Using Entity Framework
源码学习的重要性,再一次让人信服. ASP.NET MVC Application Using Entity Framework Code First 做MVC已经有段时间了,但看了一些CodePle ...
- Redis源码学习:字符串
Redis源码学习:字符串 1.初识SDS 1.1 SDS定义 Redis定义了一个叫做sdshdr(SDS or simple dynamic string)的数据结构.SDS不仅用于 保存字符串, ...
- 【Netty源码学习】ChannelPipeline(一)
ChannelPipeline类似于一个管道,管道中存放的是一系列对读取数据进行业务操作的ChannelHandler. 1.ChannelPipeline的结构图: 在之前的博客[Netty源码学习 ...
- colly源码学习
colly源码学习 colly是一个golang写的网络爬虫.它使用起来非常顺手.看了一下它的源码,质量也是非常好的.本文就阅读一下它的源码. 使用示例 func main() { c := coll ...
- Dubbo源码学习--优雅停机原理及在SpringBoot中遇到的问题
Dubbo源码学习--优雅停机原理及在SpringBoot中遇到的问题 相关文章: Dubbo源码学习文章目录 前言 主要是前一阵子换了工作,第一个任务就是解决目前团队在 Dubbo 停机时产生的问题 ...
- mongo源码学习(四)服务入口点ServiceEntryPoint
在上一篇博客mongo源码学习(三)请求接收传输层中,稍微分析了一下TransportLayer的作用,这篇来看下ServiceEntryPoint是怎么做的. 首先ServiceEntryPoint ...
随机推荐
- 函数索引引用的函数必须是immutable类型
用户在使用中,可能会用到基于函数的索引,但是函数是非 immutable 类型的,导致函数索引无法创建.如: test=# create index ind_t1 on t1(to_char(crea ...
- Zookeeper+dubbo+Springboot集成总结
1. 尽量用XML 集成,这也的Dubbo官方推荐的集成方式 自己在使用注解集成过程中发现有坑:Springmvc包扫描和dubbo包扫描冲突,导致消费端一直拿不到代理对象(null),非常蛋疼,所以 ...
- LFS(Linux From Scratch)构建过程全记录(七):进入Chroot并构建临时工具
写在前面 本章将完成临时系统构建的最后缺失部分和各种包构建所需的工具. 解决了所有循环依赖关系后,就可以使用与主机操作系统完全隔离的"chroot"环境进行构建. 注意:接下来的指 ...
- 邮箱的代理发送Send as权限不生效
邮箱的代理发送Sendas权限不生效 最近,有需求为用户添加其它邮箱的代理发送Sendas权限.在Exchange的管理单元里添加完毕后,发现没有效果,客户端提示你没有权限以用户的名义发送邮件 ...
- 使用J2EE 登录实例开发
我们先了解下Servlet的生命周期 Servlet部署在容器里,其生命周期由容器管理. 概括为以下几个阶段: 1)容器加载Servlet类. 当第一次有Web客户请求Servlet服务或当Web服务 ...
- Django 之路由层
一.Django 请求周期生命流程图 首先,用户在浏览器中输入URL,发送一个GET 或 POST 方法的request 请求. Django 中封装了socket 的WSGI 服务器,监听端口接受这 ...
- 解决RHEL7.3系统找不到yum命令,无法使用YUM源的问题
前言 RHEL的YUM源需要注册用户才能更新使用,由于CentOS和RHEL基本没有区别,并且CentOS已经被REHL收购.所以将RHEL的YUM源替换为CentOS即可. [root@NISEC- ...
- 使用 openssl 生成 https 证书, 并在 nginx 中配置 https
创建一个私钥 openssl genrsa -des3 -out server.key 2048 注意:这一步需要输入私钥,否则会提示:You must type in 4 to 1023 chara ...
- 了解Elasticsearch写入磁盘的数据
文章转载自: https://mp.weixin.qq.com/s?__biz=MzI2NDY1MTA3OQ==&mid=2247484171&idx=1&sn=985a71a ...
- Kubernetes(k8s)为容器设置启动时要执行的命令和参数
创建 Pod 时设置命令及参数 创建 Pod 时,可以为其下的容器设置启动时要执行的命令及其参数.如果要设置命令,就填写在配置文件的 command 字段下,如果要设置命令的参数,就填写在配置文件的 ...