【时快讯】【Seata解析】TC处理全局事务和分支事务原理详解
本文接文章《Seata解析-TC处理全局事务和分支事务原理详解之全局事务开启和分支事务注册》继续介绍TC对请求的处理。本文将介绍分支状态报告请求和全局事务报告请求。
文章目录
(资料图)
一、分支状态报告请求二、全局事务报告请求1、提交全局事务2、回滚全局事务3、其他事务状态4、总结
一、分支状态报告请求
分支状态报告请求的消息类型是MessageType.TYPE_BRANCH_STATUS_REPORT,请求对象为BranchReportRequest,该请求由RM发起。该请求的作用是报告某个分支事务的状态,TC收到后更改对应BranchSession对象的状态。 处理该请求通过模板方法调用了DefaultCoordinator.doBranchReport:
protected void doBranchReport(BranchReportRequest request, BranchReportResponse response, RpcContext rpcContext) throws TransactionException {//调用DefaultCore的branchReport方法 core.branchReport(request.getBranchType(), request.getXid(), request.getBranchId(), request.getStatus(), request.getApplicationData()); }
在doBranchReport方法中又调用了DefaultCore的branchReport方法,在branchReport方法中又调用了其他方法,最终调用到ATCore的branchReport方法:
public void branchReport(BranchType branchType, String xid, long branchId, BranchStatus status, String applicationData) throws TransactionException {//根据XID得到全局事务对象GlobalSession GlobalSession globalSession = assertGlobalSessionNotNull(xid, true); //根据branchId得到分支事务对象branchSession BranchSession branchSession = globalSession.getBranch(branchId); if (branchSession == null) {throw new BranchTransactionException(BranchTransactionNotExist, String.format("Could not found branch session xid = %s branchId = %s", xid, branchId)); } //添加监听器 globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); //更改分支事务状态,本请求的处理核心在下面这个方法中 globalSession.changeBranchStatus(branchSession, status); if (LOGGER.isInfoEnabled()) {LOGGER.info("Report branch status successfully, xid = {}, branchId = {}", globalSession.getXid(), branchSession.getBranchId()); } }
branchReport方法在最后调用了GlobalSession的changeBranchStatus方法,这个方法也非常简单,就是直接更改了分支事务的状态:
public void changeBranchStatus(BranchSession branchSession, BranchStatus status) throws TransactionException {//设置分支事务对象的状态 branchSession.setStatus(status); //通知监听器,监听器的处理后续文章介绍 for (SessionLifecycleListener lifecycleListener : lifecycleListeners) {lifecycleListener.onBranchStatusChange(this, branchSession, status); } }
分支状态报告请求的处理还是比较简单的,总起来说就是找到分支事务对象BranchSession,然后直接修改其状态。
二、全局事务报告请求
全局事务报告请求的消息类型是MessageType.TYPE_GLOBAL_REPORT,请求对象为GlobalReportRequest,该请求由TM发起。该请求的作用是报告某个全局事务的状态,TC收到后更改该全局事务的状态。 全局事务报告请求的处理通过模板方法调用了协调器对象DefaultCoordinator的doGlobalReport方法,之后在doGlobalReport方法里面进一步调用到DefaultCore的globalReport方法:
public GlobalStatus globalReport(String xid, GlobalStatus globalStatus) throws TransactionException {//根据XID找到全局事务 GlobalSession globalSession = SessionHolder.findGlobalSession(xid); if (globalSession == null) {return globalStatus; } //添加监听器 globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); doGlobalReport(globalSession, xid, globalStatus); return globalSession.getStatus(); } @Override public void doGlobalReport(GlobalSession globalSession, String xid, GlobalStatus globalStatus) throws TransactionException {if (globalSession.isSaga()) {//getCore(BranchType.SAGA)方法返回的对象是SagaCore,现在使用的是AT模式,不知道为什么调用SagaCore对象 getCore(BranchType.SAGA).doGlobalReport(globalSession, xid, globalStatus); } }
globalReport和doGlobalReport两个方法也非常简单,就是找到全局事务对象,然后调用SagaCore的doGlobalReport方法:
public void doGlobalReport(GlobalSession globalSession, String xid, GlobalStatus globalStatus) throws TransactionException {//全局事务提交 if (GlobalStatus.Committed.equals(globalStatus)) {removeAllBranches(globalSession); SessionHelper.endCommitted(globalSession); LOGGER.info("Global[{}] committed", globalSession.getXid()); } //全局事务回滚,Finished未知,等分析TM的时候在介绍该状态 else if (GlobalStatus.Rollbacked.equals(globalStatus) || GlobalStatus.Finished.equals(globalStatus)) {removeAllBranches(globalSession); SessionHelper.endRollbacked(globalSession); LOGGER.info("Global[{}] rollbacked", globalSession.getXid()); } //其他状态 else {globalSession.changeStatus(globalStatus); LOGGER.info("Global[{}] reporting is successfully done. status[{}]", globalSession.getXid(), globalSession.getStatus()); if (GlobalStatus.RollbackRetrying.equals(globalStatus) || GlobalStatus.TimeoutRollbackRetrying.equals(globalStatus) || GlobalStatus.UnKnown.equals(globalStatus)) {globalSession.queueToRetryRollback(); LOGGER.info("Global[{}] will retry rollback", globalSession.getXid()); } else if (GlobalStatus.CommitRetrying.equals(globalStatus)) {globalSession.queueToRetryCommit(); LOGGER.info("Global[{}] will retry commit", globalSession.getXid()); } } }
可以看到上面分了三种情况处理全局事务状态,下面我们也分三种情况介绍处理逻辑。
1、提交全局事务
提交全局事务调用了两个方法:removeAllBranches和SessionHelper.endCommitted。 removeAllBranches方法将GlobalSession对象中的分支事务集合清空,同时释放分支事务对数据库记录加的锁,释放锁其实就是将上一篇文章介绍的BucketLockMap对象中的数据库记录和加锁的事务id删除。SessionHelper.endCommitted将修改GlobalSession对象中的状态为已提交(GlobalStatus.Committed):
private void removeAllBranches(GlobalSession globalSession) throws TransactionException {//得到所有的分支事务 ArrayListbranchSessions = globalSession.getSortedBranches(); for (BranchSession branchSession : branchSessions) {globalSession.removeBranch(branchSession); } } //下面是GlobalSession中的方法 public void removeBranch(BranchSession branchSession) throws TransactionException {for (SessionLifecycleListener lifecycleListener : lifecycleListeners) {lifecycleListener.onRemoveBranch(this, branchSession); } //释放分支事务加的锁 branchSession.unlock(); //删除分支事务 remove(branchSession); } public boolean remove(BranchSession branchSession) {return branchSessions.remove(branchSession); }
下面是SessionHelper.endCommitted方法:
public static void endCommitted(GlobalSession globalSession) throws TransactionException {globalSession.changeStatus(GlobalStatus.Committed); globalSession.end(); } //下面是GlobalSession的方法 public void end() throws TransactionException {// Clean locks first clean(); for (SessionLifecycleListener lifecycleListener : lifecycleListeners) {lifecycleListener.onEnd(this); } } public void clean() throws TransactionException {//下面的代码也是释放分支事务的锁,因为removeAllBranches方法已经释放过,所以下面的方法是空执行 LockerManagerFactory.getLockManager().releaseGlobalSessionLock(this); }
2、回滚全局事务
回滚全局事务的首先也是执行removeAllBranches方法,所以第一步也是释放分支事务加的锁。下面我们重点看一下SessionHelper.endRollbacked:
public static void endRollbacked(GlobalSession globalSession) throws TransactionException {GlobalStatus currentStatus = globalSession.getStatus(); //isTimeoutGlobalStatus检查当前状态是否是超时的状态 if (isTimeoutGlobalStatus(currentStatus)) {globalSession.changeStatus(GlobalStatus.TimeoutRollbacked); } else {globalSession.changeStatus(GlobalStatus.Rollbacked); } globalSession.end(); } public static boolean isTimeoutGlobalStatus(GlobalStatus status) {return status == GlobalStatus.TimeoutRollbacked || status == GlobalStatus.TimeoutRollbackFailed || status == GlobalStatus.TimeoutRollbacking || status == GlobalStatus.TimeoutRollbackRetrying; }
endRollbacked方法首先设置事务的状态,然后调用了globalSession.end方法,globalSession.end方法与提交全局事务中调用的是同一个,都是用于释放分支事务锁的。 endRollbacked方法在修改全局事务状态前,会比较当前事务的状态,如果当前事务状态是超时,则设置事务状态为超时回滚成功,如果不是则设置事务状态为回滚成功。 至于全局事务的状态的流转,则在分析完RM和TM之后在介绍。
3、其他事务状态
如果请求中的事务状态不是前面两种,则根据请求直接修改GlobalSession的状态,之后是一个分支判断,下面重点看一下分支判断:
//事务回滚中、超时回滚重试中、未知状态if (GlobalStatus.RollbackRetrying.equals(globalStatus) || GlobalStatus.TimeoutRollbackRetrying.equals(globalStatus) || GlobalStatus.UnKnown.equals(globalStatus)) {globalSession.queueToRetryRollback(); LOGGER.info("Global[{}] will retry rollback", globalSession.getXid()); } //提交重试else if (GlobalStatus.CommitRetrying.equals(globalStatus)) {globalSession.queueToRetryCommit(); LOGGER.info("Global[{}] will retry commit", globalSession.getXid()); }
分支判断中分别调用了queueToRetryRollback和queueToRetryCommit。 queueToRetryRollback:
public void queueToRetryRollback() throws TransactionException {//添加监听器 this.addSessionLifecycleListener(SessionHolder.getRetryRollbackingSessionManager()); //seata提供了回滚重试管理器,下面这行代码将本全局事务对象添加到回滚重试管理器中 //回滚重试管理器后面文章介绍 SessionHolder.getRetryRollbackingSessionManager().addGlobalSession(this); GlobalStatus currentStatus = this.getStatus(); //设置事务状态 if (SessionHelper.isTimeoutGlobalStatus(currentStatus)) {this.changeStatus(GlobalStatus.TimeoutRollbackRetrying); } else {this.changeStatus(GlobalStatus.RollbackRetrying); } }
queueToRetryCommit:
public void queueToRetryCommit() throws TransactionException {//设置监听器 this.addSessionLifecycleListener(SessionHolder.getRetryCommittingSessionManager()); //将本全局事务对象添加到重试提交管理器中 //重试提交管理器后面文章介绍 SessionHolder.getRetryCommittingSessionManager().addGlobalSession(this); //设置事务状态 this.changeStatus(GlobalStatus.CommitRetrying); }
从queueToRetryRollback和queueToRetryCommit两个方法中看出,当事务需要重试提交或者重试回滚时,都将全局事务对象加入到对应管理器中,由管理器进行后续处理。关于管理器,后面的文章详细分析。
4、总结
总的来说,在处理全局事务报告请求时,分了如下几种情况: (1)事务提交,更改事务状态为已提交,释放分支事务加的数据库记录锁; (2)事务回滚,释放分支事务加的数据库记录锁,更改事务状态为回滚成功或者超时回滚成功; (3)事务回滚中、超时回滚重试中、未知状态,更改事务状态,将GlobalSession对象添加到回滚重试管理器; (4)提交重试,更改事务状态,将GlobalSession对象添加到重试提交管理器; (5)其他状态,更改事务状态。
标签:
相关推荐:
- []【时快讯】【Seata解析】TC处理全局事务和分支事务原理详解
- []网上订票抢票攻略 360极速抢票浏览器使用教程_世界快讯
- []Chrome缓存文件路径在哪?Chrome浏览器缓存文件路径查看方法
- []Insert Into select与Select Into哪个更快?数据库中SELECTINTO和SELECTO的区别 环球新动态
- []全球热文:hadoop3.2镜像挂载文件(附下载)
- []天天消息!汇编中DOSBox怎么使用?新建文件夹MyASM_Code(PSP)的使用方法
- []AllocateHWnd函数是如何传入消息的?AllocateHWnd函数的类型:视焦点讯
- []【新视野】通信网中常见的普通光缆有哪些?GYTA型光缆的结构及结构
最新新闻:
- ListView怎么优化?ListView的四种优化方式及使用方法
- 【新视野】通信网中常见的普通光缆有哪些?GYTA型光缆的结构及结构
- 天天消息!汇编中DOSBox怎么使用?新建文件夹MyASM_Code(PSP)的使用方法
- 【软件设计】软件易维护性的因素有哪些?-当前关注
- Apex启动器最新版怎么下载?酷派7295A青春版刷机包
- 全球球精选!火狐浏览器如何安装flash_player?安装步骤
- 天天日报丨mysql怎么批量添加卡号?mysql数据库卡号卡密批量生成写入验证
- 网上订票抢票攻略 360极速抢票浏览器使用教程_世界快讯
- 【时快讯】【Seata解析】TC处理全局事务和分支事务原理详解
- 天天动态:序列号(Serial):SourceInsight的实用技巧
- 高频开关电源原理是什么?开瑞高频开关电源原理及增长趋势分析-世界今头条
- 【世界快播报】餐厅吊灯高度怎么调?餐厅吊灯的正确安装高度
- Insert Into select与Select Into哪个更快?数据库中SELECTINTO和SELECTO的区别 环球新动态
- Chrome缓存文件路径在哪?Chrome浏览器缓存文件路径查看方法
- 三星tabs怎么样?三星GALAXYTabT805C(4G版)平板电脑评测
- 全球微动态丨如何看K线图?史上最全K线经典组合形态解析
- 全球聚焦:python多线程实现访问页面升级?python使用多线程不断刷新网页的方法
- 天天速递!IE8以上的localstorage存储内容有哪些?sessionstorage存储内容介绍
- 如何利用大数据实现精准营销?会员管理中用户数据的流转及应用解释 世界速讯
- 全球观点:国产FPS《边境》玩法公开!多地图无重力太空战斗
- AllocateHWnd函数是如何传入消息的?AllocateHWnd函数的类型:视焦点讯
- 全球热门:《口袋妖怪》精灵宝可梦let'sgo皮卡丘伊布存档修改器教程
- 1加6t是什么牌子的手机?一加手机品牌介绍_全球简讯
- 全球热文:hadoop3.2镜像挂载文件(附下载)
- 观天下!vim的智能补全方式——CTRL-N和CTRL
- 卫生间排气扇价格是多少?卫生间排气扇价格及安装
- 焦点信息:excel如何制作简版出库表?excel制作简版出库表
- JVM在字节码上怎么运用?JVM在字节码上的使用方法
- QLV格式如何转换成MP4?QLV格式转换成MP4的方法_天天最资讯
- 如何用PS制作属于自己的个性签名档?用PS制作属于自己的个性签名档教程
- 苹果8怎么查看手机常去位置?详细操作步骤:今日快讯
- 索尼a330评测详解 新款套机镜头详解
- 世界要闻:电脑启动怎么选择启动模式?解决方法在这里
- 支付宝商家收款码怎么开通?开通方法来了
- 鸿蒙os2.0系统支持机型有哪些?适配机型介绍
- 克苏鲁FPS《原谅我父亲2》Steam页面上线 暂不支持中文
- 焦点滚动:梦三国服务器每天维护几次?玩家数量逐渐减少的原因是什么?
- 全球观速讯丨育碧免费FPS《不羁联盟》4月14日开启封闭测试
- 联想平板电脑价格多少钱?联想平板电脑价格及型号:今热点
- 4月6日伦敦金属交易所(LME)铅库存26375吨 每日快看
- 【人脸表情识别】pytorch处理CK+数据集:每日简讯
- FPS+策略游戏《Silica》公布 Steam页面已上线-全球新要闻
- 环球动态:如何实现双显示器拼接?实现双显示器拼接方法
- oppo手机上面HD如何取消?oppo手机上面HD取消方法 天天资讯
- 角位移传感器怎么安装?角位移传感器安装方法详解:全球热议
- 天天快看:歪歪如何申请短位ID?歪歪语音使用文字聊天的方法
- QQ怎样绑定密保手机?QQ绑定密保手机的方法
- 如何查询电脑上的IP地址?我的ip地址查询方法
- 电脑安装xp和windows7双系统有什么区别?Xp和Win7双系统怎么安装?_全球通讯
- 世界简讯:javascript查找并且倒排序的方法 mongodb常用操作命令大全
- Ajax:异步的JavaScript和XML的同源策略
- 信息:【干货】Python与STAT时间日期转换问题
- 电脑安装xp和windows7双系统有什么区别?Xp和Win7双系统怎么安装?_全球通讯
- IBM发布全新z16和LinuxONE 4的单机柜版本_观察
- 天天速看:iPhone 8以上都能用!中国广电5G已全面支持iPhone
- RTX3060Ti游戏神卡2169元封神!
- 小米13 Ultra外观曝光:爆料全中!
- 森歌集成灶怎么样?三方面出发谈谈它的那些事儿 独家
- 高华科技上交所公开招股
- 小岛秀夫转发自己的《芭比》海报:谁不喜欢粉粉嫩嫩呢:天天简讯
- 《RE4RE》佣兵模式已上线 未来或加入艾达王\威斯克
- 全球观热点:XGP 5月首发游戏阵容 《红霞岛》领衔
- 谷歌Stadia服务关闭 项目负责人现已离开公司
- 【世界播资讯】《刺客信条:幻景》菜单栏截图 有令人兴奋的新功能
- 3D-Roguelite横版动作游戏《霓虹序列》Steam页面上线 4月21日发售
- 今日热讯:《守望先锋2》第四赛季预告 新英雄新皮肤新活动等
- 首届香港高等教育展4月15日举行 今日观点
- 全球看点:美国男子承认掰断偷走兵马俑手指 检方或只进行轻判
- 索尼:CMA对微软收购暴雪一案态度转变令人惊讶 太不合理!
- 天天观焦点:汾河二库开放吗(山西汾河二库是开放的吗)
- 雷蛇推出透明RGB滚轮游戏鼠标:搭载5G光学传感器 售价499元
- 环球观焦点:太极股份:拟提前赎回“太极转债”
- 百变星曜由你来定!星曜娘二创活动进入最终投票阶段
- 榨干《甄嬛传》价值?乐视将推85英寸甄嬛传限量电视
- 天天快消息!超大外屏亮眼!摩托罗拉razr 40 Ultra获认证