Seata(AT模式)源码解析------开启全局事务
- @GlobalTransactional注解原理复习
- 开启全局事务的源码解析
-
- [TransactionalTemplate#beginTransaction(TransactionInfo, GlobalTransaction)](#beginTransaction(TransactionInfo, GlobalTransaction))
- [DefaultGlobalTransaction#begin(int, String)](#begin(int, String))
- DefaultTransactionManager#begin
- [DefaultCoordinator#doGlobalBegin(GlobalBeginRequest, GlobalBeginResponse, RpcContext)](#doGlobalBegin(GlobalBeginRequest, GlobalBeginResponse, RpcContext))
- DefaultCore#begin
- GlobalSession.createGlobalSession
- GlobalSession#begin()
在《Seata(AT模式)源码解析------@GlobalTransactional注解与@globalLock生效的原理》这一篇文章中,我们分析了Seata(AT模式)的原理,以及GlobalTransactionScanner和GlobalTransactionalInterceptor等核心类的源码。
我们分析到了TransactionalTemplate的execute方法,但是没有对execute方法中的"开启全局事务"、"分支事务处理"、"全局事务提交"、"全局事务回滚"等分支处理的代码做分析,我们后续的文章就分别对这些分支处理逐一做详细的分析。
@GlobalTransactional注解原理复习

- 当我们的某个方法使用@GlobalTransactional注解修饰,就会被GlobalTransactionScanner扫描到,生成AOP代理对象。
- 当我们调用该方法时,会进入指定的AOP拦截器GlobalTransactionalInterceptor,GlobalTransactionalInterceptor会调用TransactionalTemplate的execute方法进行处理
- TransactionalTemplate的execute方法在try-catch代码块中事务的模板代码:开启全局事务、执行业务逻辑、全局事务回滚(如果报错)、全局事务提交。
开启全局事务的源码解析
TransactionalTemplate#beginTransaction(TransactionInfo, GlobalTransaction)
java
private void beginTransaction(TransactionInfo txInfo, GlobalTransaction tx) throws TransactionalExecutor.ExecutionException {
try {
// 调用DefaultGlobalTransaction#begin(int, String)方法开启全局事务
tx.begin(txInfo.getTimeOut(), txInfo.getName());
} catch () {}
}
TransactionalTemplate的beginTransaction会调用DefaultGlobalTransaction的begin方法开启全局事务。

DefaultGlobalTransaction#begin(int, String)
java
@Override
public void begin(int timeout, String name) throws TransactionException {
// 请求TC开启全局事务,返回全局事务ID(xid)
xid = transactionManager.begin(null, null, name, timeout);
// xid绑定到ThreadLocal
RootContext.bind(xid);
}
DefaultGlobalTransaction的begin方法就是调用transactionManager的begin方法请求TC开启全局事务,TC会返回一个xid,然后DefaultGlobalTransaction把xid存入到ThreadLocal中与当前线程绑定。

DefaultTransactionManager#begin
java
public String begin(String applicationId, String transactionServiceGroup, String name, int timeout)
throws TransactionException {
GlobalBeginRequest request = new GlobalBeginRequest();
request.setTransactionName(name);
request.setTimeout(timeout);
// 请求TC开启全局事务
GlobalBeginResponse response = (GlobalBeginResponse) syncCall(request);
// 返回全局事务ID(xid)
return response.getXid();
}
DefaultTransactionManager的begin方法调用syncCall方法请求TC开启全局事务,然后返回response对象中的xid。

syncCall方法会通过Netty发送请求。
java
private AbstractTransactionResponse syncCall(AbstractTransactionRequest request) throws TransactionException {
return (AbstractTransactionResponse) TmNettyRemotingClient.getInstance().sendSyncRequest(request);
}

DefaultCoordinator#doGlobalBegin(GlobalBeginRequest, GlobalBeginResponse, RpcContext)
java
@Override
protected void doGlobalBegin(GlobalBeginRequest request, GlobalBeginResponse response, RpcContext rpcContext)
throws TransactionException {
// core.begin(...)开启全局事务,返回xid
// 然后把xid设置到response对象钟
response.setXid(core.begin(rpcContext.getApplicationId(), rpcContext.getTransactionServiceGroup(),
request.getTransactionName(), request.getTimeout()));
}
TC接收到请求后,会调用DefaultCoordinator的doGlobalBegin方法进行处理。
doGlobalBegin方法会调用DefaultCore的begin方法记录全局事务信息,然后返回xid。

DefaultCore#begin
java
@Override
public String begin(String applicationId, String transactionServiceGroup, String name, int timeout)
throws TransactionException {
// 创建GlobalSession
// 里面会 new GlobalSession()
// GlobalSession构造方法会生成xid
GlobalSession session = GlobalSession.createGlobalSession(applicationId, transactionServiceGroup, name,
timeout);
// 这里会持久化全局事务记录
session.begin();
// 返回xid
return session.getXid();
}
DefaultCore的begin方法会调用GlobalSession的静态方法createGlobalSession创建一个GlobalSession。
创建了GlobalSession之后会调用GlobalSession的begin方法。该方法会持久化全局事务信息。

GlobalSession.createGlobalSession
java
public static GlobalSession createGlobalSession(String applicationId, String txServiceGroup, String txName,
int timeout) {
GlobalSession session = new GlobalSession(applicationId, txServiceGroup, txName, timeout);
return session;
}
GlobalSession的createGlobalSession里面会new一个GlobalSession。
java
public GlobalSession(String applicationId, String transactionServiceGroup, String transactionName, int timeout) {
this.transactionId = UUIDGenerator.generateUUID();
...
this.xid = XID.generateXID(transactionId);
}
GlobalSession的构造方法会生成一个xid。

生成了GlobalSession之后,DefaultCore就会调用它的begin方法。
GlobalSession#begin()
java
@Override
public void begin() throws TransactionException {
for (SessionLifecycleListener lifecycleListener : lifecycleListeners) {
// 进入到AbstractSessionManager#onBegin方法
lifecycleListener.onBegin(this);
}
}
AbstractSessionManager#onBegin(GlobalSession)
java
@Override
public void onBegin(GlobalSession globalSession) throws TransactionException {
addGlobalSession(globalSession);
}
@Override
public void addGlobalSession(GlobalSession session) throws TransactionException {
writeSession(LogOperation.GLOBAL_ADD, session);
}
private void writeSession(LogOperation logOperation, SessionStorable sessionStorable) throws TransactionException {
// 持久化全局事务信息
if (!transactionStoreManager.writeSession(logOperation, sessionStorable)) {
// 省略...
}
}
GlobalSession的begin方法一路下来进入到了TransactionStoreManager的writeSession方法。TransactionStoreManager是一个接口,如果我们配置了seata的持久化方式时数据库,那么会进入到DataBaseTransactionStoreManager的writeSession方法。
DataBaseTransactionStoreManager#writeSession
java
@Override
public boolean writeSession(LogOperation logOperation, SessionStorable session) {
if (LogOperation.GLOBAL_ADD.equals(logOperation)) {
// 插入全局事务信息
return logStore.insertGlobalTransactionDO(SessionConverter.convertGlobalTransactionDO(session));
} else if (LogOperation.GLOBAL_UPDATE.equals(logOperation)) {
// 更新全局事务信息
return logStore.updateGlobalTransactionDO(SessionConverter.convertGlobalTransactionDO(session));
} else if (LogOperation.GLOBAL_REMOVE.equals(logOperation)) {
// 删除全局事务信息
return logStore.deleteGlobalTransactionDO(SessionConverter.convertGlobalTransactionDO(session));
} else if (LogOperation.BRANCH_ADD.equals(logOperation)) {
// 新增分支事务信息
return logStore.insertBranchTransactionDO(SessionConverter.convertBranchTransactionDO(session));
} else if (LogOperation.BRANCH_UPDATE.equals(logOperation)) {
// 更新分支事务信息
return logStore.updateBranchTransactionDO(SessionConverter.convertBranchTransactionDO(session));
} else if (LogOperation.BRANCH_REMOVE.equals(logOperation)) {
// 删除分支事务信息
return logStore.deleteBranchTransactionDO(SessionConverter.convertBranchTransactionDO(session));
} else {
throw new StoreException("Unknown LogOperation:" + logOperation.name());
}
}

java
@Override
public boolean insertGlobalTransactionDO(GlobalTransactionDO globalTransactionDO) {
String sql = LogStoreSqlsFactory.getLogStoreSqls(dbType).getInsertGlobalTransactionSQL(globalTable);
Connection conn = null;
PreparedStatement ps = null;
try {
int index = 1;
conn = logStoreDataSource.getConnection();
conn.setAutoCommit(true);
ps = conn.prepareStatement(sql);
ps.setString(index++, globalTransactionDO.getXid()); // xid
ps.setLong(index++, globalTransactionDO.getTransactionId());
ps.setInt(index++, globalTransactionDO.getStatus()); // 全局事务状态
ps.setString(index++, globalTransactionDO.getApplicationId());
ps.setString(index++, globalTransactionDO.getTransactionServiceGroup());
String transactionName = globalTransactionDO.getTransactionName();
transactionName = transactionName.length() > transactionNameColumnSize ?
transactionName.substring(0, transactionNameColumnSize) :
transactionName;
ps.setString(index++, transactionName);
ps.setInt(index++, globalTransactionDO.getTimeout());
ps.setLong(index++, globalTransactionDO.getBeginTime());
ps.setString(index++, globalTransactionDO.getApplicationData());
return ps.executeUpdate() > 0;
} catch (SQLException e) {
throw new StoreException(e);
} finally {
IOUtil.close(ps, conn);
}
}
最终会通过jdbc的方式把当前全局事务的信息写入到global_table表。
sql
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for global_table
-- ----------------------------
DROP TABLE IF EXISTS `global_table`;
CREATE TABLE `global_table` (
`xid` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '全局事务ID',
`transaction_id` bigint(20) NULL DEFAULT NULL COMMENT '事务ID',
`status` tinyint(4) NOT NULL COMMENT '状态',
`application_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '应用ID',
`transaction_service_group` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '事务分组名',
`transaction_name` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '执行事务的方法',
`timeout` int(11) NULL DEFAULT NULL COMMENT '超时时间',
`begin_time` bigint(20) NULL DEFAULT NULL COMMENT '开始时间',
`application_data` varchar(2000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '应用数据',
`gmt_create` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`gmt_modified` datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
PRIMARY KEY (`xid`) USING BTREE,
INDEX `idx_gmt_modified_status`(`gmt_modified`, `status`) USING BTREE,
INDEX `idx_transaction_id`(`transaction_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
SET FOREIGN_KEY_CHECKS = 1;
