我们知道Seata服务端TC在全局事务中需要协调TM,RM分工干活,一个全局事务的也是由多个分支事务组成的,那么TC端必须要对这些全局事务和分支事务进行管理,比如事务的创建、更新、删除...我们今天就来聊一聊Seata中的事务管理者SessionManager。

*这里为什么叫SessionManager:有博客说Seata的中的事务也叫会话,会话管理器也叫事务管理器。我们就这样叫吧

事务管理器

SessionManager

SessionManager是一个接口,我们来看它的继承关系:

*这种结构还挺常见的,Seata的Netty模块也是这样,一个顶级接口,一个抽象类,然后下面就是具体模式的实现类。

SessionLifecycleListener

  • 首先它继承了接口SessionLifecycleListener,这是一个会话生命周期的监听器,(使用了观察者模式),此接口定义了一系列要监听的事件:
public interface SessionLifecycleListener {
/**
* 监听全局事务的开启,当处理全局事务开启请求时,会调用该方法
*/
void onBegin(GlobalSession globalSession) throws TransactionException; /**
* 监听全局事务对象GlobalSession的状态变化,只要是GlobalSession的状态发生变化,就会调用该方法
*/
void onStatusChange(GlobalSession globalSession, GlobalStatus status) throws TransactionException; /**
* 监听分支事务状态的变化,在处理分支状态报告请求时,会调用该方法
*/
void onBranchStatusChange(GlobalSession globalSession, BranchSession branchSession, BranchStatus status)
throws TransactionException; /**
* 监听新的分支事务注册
*/
void onAddBranch(GlobalSession globalSession, BranchSession branchSession) throws TransactionException; /**
* 监听分支事务从全局事务对象中移除,
* 当处理全局事务回滚请求全局事务提交请求时,都会有移除分支事务的动作,因此都会触发该方法
*/
void onRemoveBranch(GlobalSession globalSession, BranchSession branchSession) throws TransactionException; /**
* 监听全局事务关闭,也就是监听GlobalSession的close方法。
* 在处理全局事务提交请求和全局事务回滚请求时,都会调用GlobalSession的close方法。
*/
void onClose(GlobalSession globalSession) throws TransactionException; /**
* 监听全局事务终止,也就是监听GlobalSession的end方法。
* 当要求全局事务提交或者回滚时,无论最后成功与否,seata都会调用GlobalSession的end方法,因此都会触发onEnd
*/
void onEnd(GlobalSession globalSession) throws TransactionException;
}

SessionManager则定义了GlobalSession状态发生变化时应该执行的动作方法

public interface SessionManager extends SessionLifecycleListener, Disposable {

    /**
* 将全局事务对象添加到会话管理器中,当全局事务异步提交或者异步回滚时,都会调用该方法
*/
void addGlobalSession(GlobalSession session) throws TransactionException; /**
* 根据XID查找GlobalSession
*/
GlobalSession findGlobalSession(String xid) ; /**
* 不同的存储模式下,本方法和上面的方法实现不同,如果存储模式是file,则两个方法完全一致,
* 如果存储模式是db,则上面的方法相当于调用findGlobalSession(xid, true)
* 如果第二个参数为true,表示返回的GlobalSession对象中带有分支事务集合
*/
GlobalSession findGlobalSession(String xid, boolean withBranchSessions); /**
* 更新事务对象的状态
*/
void updateGlobalSessionStatus(GlobalSession session, GlobalStatus status) throws TransactionException; /**
* 从管理器中移除GlobalSession
* 当异步提交重试超时时,会调用该方法
*/
void removeGlobalSession(GlobalSession session) throws TransactionException; /**
* 向GlobalSession中添加分支事务对象,当分支事务注册时,会调用该方法
*/
void addBranchSession(GlobalSession globalSession, BranchSession session) throws TransactionException; /**
* 更新分支事务状态
*/
void updateBranchSessionStatus(BranchSession session, BranchStatus status) throws TransactionException; /**
* 从全局事务中移除分支事务,当全局事务提交或者回滚时,会调用该方法
*/
void removeBranchSession(GlobalSession globalSession, BranchSession session) throws TransactionException; /**
* 返回所有的全局会话对象
*/
Collection<GlobalSession> allSessions(); /**
* 根据条件查找符合要求的GlobalSession
*/
List<GlobalSession> findGlobalSessions(SessionCondition condition); /**
* 对全局事务对象加锁,当修改全局事务对象的状态时,都会加锁
*/
<T> T lockAndExecute(GlobalSession globalSession, GlobalSession.LockCallable<T> lockCallable)
throws TransactionException;
}

AbstractSessionManager

AbstractSessionManager是下一层的封装,它有三个实现类,分别对应三种存储模式:文件,数据库和Redis

它实现了SessionManager中定义的方法,还增加了一个重要的方法:writeSession,对Session管理的方法大多都直接或间接地调用了writeSession;我们简单来看一个:

    @Override
public void removeGlobalSession(GlobalSession session) throws TransactionException {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("MANAGER[{}] SESSION[{}] {}", name, session, LogOperation.GLOBAL_REMOVE);
}
writeSession(LogOperation.GLOBAL_REMOVE, session);
}

AbstractSessionManager中还持有一个变量:事务存储管理器,也有三种实现,分别是文件,数据库和Redis。下面就继续分析它。

   /**
* The Transaction store manager.
*/
protected TransactionStoreManager transactionStoreManager;

事务存储管理器

上面我们基本了解了Seata的事务管理器,它们的作用是对Seata的事务进行管理,管理好了就要存储起来 。AbstractSessionManager这个类中还持有了TransactionStoreManager,它是真正用来实现存储事务状态的。

下面我们以RedisTransactionStoreManager为例进行分析:

RedisTransactionStoreManager



可以看到有不少insert,delete的操作,说明这里就真正将事务信息存到Redis的逻辑了,我们挑一个来看一下:

 /**
* Insert the global transaction.
* @param globalTransactionDO
* @return
*/
// GlobalTransactionDO:要插入数据库的类,和global_table的字段是一一对应的
private boolean insertGlobalTransactionDO(GlobalTransactionDO globalTransactionDO) {
// 获取全局事务的键
String globalKey = buildGlobalKeyByTransactionId(globalTransactionDO.getTransactionId());
//使用了Jedis和Pipeline
try (Jedis jedis = JedisPooledFactory.getJedisInstance(); Pipeline pipelined = jedis.pipelined()) {
Date now = new Date();
//构建要插入的DO
globalTransactionDO.setGmtCreate(now);
globalTransactionDO.setGmtModified(now);
//通过pipeline执行
pipelined.hmset(globalKey, BeanUtils.objectToMap(globalTransactionDO));
pipelined.rpush(buildGlobalStatus(globalTransactionDO.getStatus()), globalTransactionDO.getXid());
pipelined.sync();
return true;
} catch (Exception ex) {
throw new RedisException(ex);
}
}

TO BE CONTINUE...

Seata源码分析——SessionManager的更多相关文章

  1. Seata源码分析(一). AT模式底层实现

    目录 GlobalTransactionScanner 继承AbstractAutoProxyCreator 实现InitializingBean接口 写在最后 以AT为例,我们使用Seata时只需要 ...

  2. Seata AT 模式启动源码分析

    从上一篇文章「分布式事务中间件Seata的设计原理」讲了下 Seata AT 模式的一些设计原理,从中也知道了 AT 模式的三个角色(RM.TM.TC),接下来我会更新 Seata 源码分析系列文章. ...

  3. Shiro 源码分析

    http://my.oschina.net/huangyong/blog/215153 Shiro 是一个非常优秀的开源项目,源码非常值得学习与研究. 我想尝试做一次 不一样 的源码分析:源码分析不再 ...

  4. shiro实现无状态的会话,带源码分析

    转载请在页首明显处注明作者与出处 朱小杰      http://www.cnblogs.com/zhuxiaojie/p/7809767.html 一:说明 在网上都找不到相关的信息,还是翻了大半天 ...

  5. Shiro源码分析之SecurityManager对象获取

    目录 SecurityManager获取过程 1.SecurityManager接口介绍 2.SecurityManager实例化时序图 3.源码分析 4.总结 @   上篇文章Shiro源码分析之获 ...

  6. SparkThriftServer 源码分析

    目录 版本 起点 客户端--Beeline 服务端 Hive-jdbc TCLIService.Iface客户端请求 流程 SparkThrift 主函数HiveThriftServer2 Thrif ...

  7. AndroidPn源码分析(二)

    接上篇: (一)客户端与服务器建立连接 上一篇写到ClientSession createClientSession这里,创建一个客户端的session.在SessionManager类中创建了ses ...

  8. 元旦在家撸了两天Seata源码,你们是咋度过的呢?

    撸Seata源码 2020年12月31日晚23点30分,我发了2020年的最后一个朋友圈:假期吃透Seata源码,有组队的吗? 不少小伙伴都来点赞了, 其中也包括Seata项目的发起人--季敏大佬哦! ...

  9. ABP源码分析一:整体项目结构及目录

    ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...

随机推荐

  1. 前端每日实战:96# 视频演示如何用纯 CSS 和 D3 创作一艘遨游太空的宇宙飞船

    效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/oMqNmv 可交互视频 此视频是可 ...

  2. 在 M1 Mac 上开发 .NET MAUI (iOS)

    在 M1 Mac 上开始使用 .NET MAUI 开发 (iOS) 的最简单方法. .NET 多平台应用程序 UI (.NET MAUI) 是一个跨平台框架,用于使用 C# 和 XAML 创建本机移动 ...

  3. 以ARM和RISC-V为内核的单片机写寄存器

    我以为这是个很简单的问题,没想到还有一些初学者不会.可能他们也是跟我一样是直接学的如何操作单片机并没有学微机原理么. ARM和RISC-V的机器的系统架构都是哈佛结构的,意思是程序存储器.数据存储器和 ...

  4. 安卓记账本开发学习day9之依赖导入失败

    根据上一篇文章导入依赖,在一些旧版本的AS上能正常完成,但是我下载最新的AS以后无法正常导入 同步的时候控制台报 Build file 'C:\CS\AndroidStudioProjects\tal ...

  5. 代码源 BFS练习1

    BFS练习1 http://oj.daimayuan.top/course/11/problem/147 题目 思路 四个方向进行BFS 注意:此题读写量大,cin会被卡 代码 #include &l ...

  6. petite-vue源码剖析-逐行解读@vue-reactivity之Map和Set的reactive

    本篇我们会继续探索reactive函数中对Map/WeakMap/Set/WeakSet对象的代理实现. Map/WeakMap/Set/WeakSet的操作 由于WeakMap和WeakSet分别是 ...

  7. 使用 VS Code 撰写 Markdown 文档

    众所周知, VS Code 是微软和社区一起开发的一款很优秀的高级代码编辑器.它不仅可以写出一手好代码,还能写出一篇好文章.利用 Markdown 就可以写出一篇排版美观的技术文章了. 而 Markd ...

  8. v74.01 鸿蒙内核源码分析(编码方式篇) | 机器指令是如何编码的 | 百篇博客分析OpenHarmony源码

    本篇关键词:指令格式.条件域.类型域.操作域.数据指令.访存指令.跳转指令.SVC(软件中断) 内核汇编相关篇为: v74.01 鸿蒙内核源码分析(编码方式) | 机器指令是如何编码的 v75.03 ...

  9. JS_进阶-遍历对象属性、数组输出、Math、正则表达式匹配

    1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="U ...

  10. 『现学现忘』Git基础 — 14、Git基础操作的总结与补充

    目录 1.Git本地版本库结构 2.Git常用操作方法 3.补充:添加多个文件到暂存区 4.补充:提交操作未写备注 5.补充:从工作区直接提交到版本库 1.Git本地版本库结构 如下图所示: 工作区( ...