一阶段前三篇:seata 2阶段提交实现代码-笔记1-CSDN博客
回归下AT模式:
两阶段提交协议的演变:
- 一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
- 二阶段:
- 提交异步化,非常快速地完成。
- 回滚通过一阶段的回滚日志进行反向补偿。
二阶段-提交
篇1整理过transactionalTemplate.execute方法,就是一阶段执行完成后如果没有业务异常,TM会向TC提交全局事务请求。
// 4. everything is fine, commit.
commitTransaction(tx, txInfo);
内部会执行 tx.commit();
>> io.seata.tm.api.DefaultGlobalTransaction#commit()
java
@Override
public void commit() throws TransactionException {
if (role == GlobalTransactionRole.Participant) {
// Participant has no responsibility of committing
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Ignore Commit(): just involved in global transaction [{}]", xid);
}
return;
}
assertXIDNotNull();
if (LOGGER.isInfoEnabled()) {
LOGGER.info("transaction {} will be commit", xid);
}
int retry = COMMIT_RETRY_COUNT <= 0 ? DEFAULT_TM_COMMIT_RETRY_COUNT : COMMIT_RETRY_COUNT;
try {
while (retry > 0) {
try {
retry--;
status = transactionManager.commit(xid);
break;
} catch (Throwable ex) {
LOGGER.error("Failed to report global commit [{}],Retry Countdown: {}, reason: {}", this.getXid(), retry, ex.getMessage());
if (retry == 0) {
throw new TransactionException("Failed to report global commit", ex);
}
}
}
} finally {
if (xid.equals(RootContext.getXID())) {
suspend(true);
}
}
if (LOGGER.isInfoEnabled()) {
LOGGER.info("[{}] commit status: {}", xid, status);
}
}
这个主要逻辑:1 判断角色,是TM才会执行 。
2 重复发起调用TC发起事务提交请求,最多5次。
transactionManager.commit() 底层也是netty的 请求GlobalCommitRequest,返回:GlobalCommitResponse
TC的处理全局事务提交
server 对于GlobalCommitRequest 请求处理逻辑在
io.seata.server.coordinator.DefaultCoordinator#doGlobalCommit
java
@Override
protected void doGlobalCommit(GlobalCommitRequest request, GlobalCommitResponse response, RpcContext rpcContext)
throws TransactionException {
MDC.put(RootContext.MDC_KEY_XID, request.getXid());
response.setGlobalStatus(core.commit(request.getXid()));//设置全局事务状态
}
接下来看看commit 方法,其中AT模式是异步提交。
java
@Override
public GlobalStatus commit(String xid) throws TransactionException {
//根据xid获取全局事务对象GlobalSession,db模式下会查询global_table表
GlobalSession globalSession = SessionHolder.findGlobalSession(xid);
if (globalSession == null) {//为空直接返回finished状态
return GlobalStatus.Finished;
}
if (globalSession.isTimeout()) {
LOGGER.info("TC detected timeout, xid = {}", globalSession.getXid());
return GlobalStatus.TimeoutRollbacking;
}
// just lock changeStatus
boolean shouldCommit = SessionHolder.lockAndExecute(globalSession, () -> {
boolean shouldCommitNow = false;
if (globalSession.getStatus() == GlobalStatus.Begin) {
// Highlight: Firstly, close the session, then no more branch can be registered.
//关闭,防止有新的branch注册进来
globalSession.close();
if (globalSession.canBeCommittedAsync()) {//只有AT可以异步提交
globalSession.asyncCommit();
MetricsPublisher.postSessionDoneEvent(globalSession, GlobalStatus.Committed, false, false);
} else {
globalSession.changeGlobalStatus(GlobalStatus.Committing);
shouldCommitNow = true;
}
//clean session after changing status successfully.
globalSession.clean();
}
return shouldCommitNow;
});
if (shouldCommit) {//同步提交 TCC
boolean success = doGlobalCommit(globalSession, false);
//If successful and all remaining branches can be committed asynchronously, do async commit.
if (success && globalSession.hasBranch() && globalSession.canBeCommittedAsync()) {
globalSession.asyncCommit();
return GlobalStatus.Committed;
} else {
return globalSession.getStatus();
}
} else {
return globalSession.getStatus() == GlobalStatus.AsyncCommitting ? GlobalStatus.Committed : globalSession.getStatus();
}
}
在看看异步提交
public void asyncCommit() throws TransactionException {
changeGlobalStatus(GlobalStatus.AsyncCommitting);
}
java
public void changeGlobalStatus(GlobalStatus status) throws TransactionException {
if (GlobalStatus.Rollbacking == status || GlobalStatus.TimeoutRollbacking == status) {
LockerManagerFactory.getLockManager().updateLockStatus(xid, LockStatus.Rollbacking);
}
SessionHolder.getRootSessionManager().onStatusChange(this, status);
// set session status after update successfully
this.status = status;
for (SessionLifecycleListener lifecycleListener : lifecycleListeners) {
lifecycleListener.onStatusChange(this, status);
}
}
更新:global_table 表状态
可以看到AT模式异步提交只是更新了状态,实际执行在哪里呢?io.seata.server.coordinator.DefaultCoordinator#init,里面指定了线程池,每隔1秒执行
java
public void init() {
retryRollbacking.scheduleAtFixedRate(
() -> SessionHolder.distributedLockAndExecute(RETRY_ROLLBACKING, this::handleRetryRollbacking), 0,
ROLLBACKING_RETRY_PERIOD, TimeUnit.MILLISECONDS);
retryCommitting.scheduleAtFixedRate(
() -> SessionHolder.distributedLockAndExecute(RETRY_COMMITTING, this::handleRetryCommitting), 0,
COMMITTING_RETRY_PERIOD, TimeUnit.MILLISECONDS);
//异步提交在这里处理
asyncCommitting.scheduleAtFixedRate(
() -> SessionHolder.distributedLockAndExecute(ASYNC_COMMITTING, this::handleAsyncCommitting), 0,
ASYNC_COMMITTING_RETRY_PERIOD, TimeUnit.MILLISECONDS);
timeoutCheck.scheduleAtFixedRate(
() -> SessionHolder.distributedLockAndExecute(TX_TIMEOUT_CHECK, this::timeoutCheck), 0,
TIMEOUT_RETRY_PERIOD, TimeUnit.MILLISECONDS);
undoLogDelete.scheduleAtFixedRate(
() -> SessionHolder.distributedLockAndExecute(UNDOLOG_DELETE, this::undoLogDelete),
UNDO_LOG_DELAY_DELETE_PERIOD, UNDO_LOG_DELETE_PERIOD, TimeUnit.MILLISECONDS);
}
这个distributedLockAndExecute先要获取分布式锁。
java
/**
* Execute the function after get the distribute lock
*
* @param key the distribute lock key
* @param func the function to be call
* @return whether the func be call
*/
public static boolean distributedLockAndExecute(String key, NoArgsFunc func) {
boolean lock = false;
try {
if (lock = acquireDistributedLock(key)) {
func.call();
}
} catch (Exception e) {
LOGGER.error("Exception running function with key = {}", key, e);
} finally {
if (lock) {
try {
SessionHolder.releaseDistributedLock(key);
} catch (Exception ex) {
LOGGER.warn("release distribute lock failure, message = {}", ex.getMessage(), ex);
}
}
}
return lock;
}
底层依赖seata库的distributed_lock表。再看看具体实现handleAsyncCommitting
java
/**
* Handle async committing.
*/
protected void handleAsyncCommitting() {
//获取AsyncCommiting状态GlobalSession
SessionCondition sessionCondition = new SessionCondition(GlobalStatus.AsyncCommitting);
Collection<GlobalSession> asyncCommittingSessions =
SessionHolder.getRootSessionManager().findGlobalSessions(sessionCondition);
if (CollectionUtils.isEmpty(asyncCommittingSessions)) {
return;
}
SessionHelper.forEach(asyncCommittingSessions, asyncCommittingSession -> {
try {//处理
core.doGlobalCommit(asyncCommittingSession, true);
} catch (TransactionException ex) {
LOGGER.error("Failed to async committing [{}] {} {}", asyncCommittingSession.getXid(), ex.getCode(), ex.getMessage(), ex);
}
});
}
还是调用了同步的方法core.doGlobalCommit。
java
public boolean doGlobalCommit(GlobalSession globalSession, boolean retrying) throws TransactionException {
boolean success = true;
// start committing event 发布事件
MetricsPublisher.postSessionDoingEvent(globalSession, retrying);
if (globalSession.isSaga()) {
success = getCore(BranchType.SAGA).doGlobalCommit(globalSession, retrying);
} else {// 获取全部的分支事务
List<BranchSession> branchSessions = globalSession.getSortedBranches();
Boolean result = SessionHelper.forEach(branchSessions, branchSession -> {
// if not retrying, skip the canBeCommittedAsync branches
if (!retrying && branchSession.canBeCommittedAsync()) {
return CONTINUE;
}
BranchStatus currentStatus = branchSession.getStatus();
if (currentStatus == BranchStatus.PhaseOne_Failed) {//一阶段失败,删除分支
SessionHelper.removeBranch(globalSession, branchSession, !retrying);
return CONTINUE;
}
try {//发送rpc 通知RM删除undo_log
BranchStatus branchStatus = getCore(branchSession.getBranchType()).branchCommit(globalSession, branchSession);
if (isXaerNotaTimeout(globalSession,branchStatus)) {
LOGGER.info("Commit branch XAER_NOTA retry timeout, xid = {} branchId = {}", globalSession.getXid(), branchSession.getBranchId());
branchStatus = BranchStatus.PhaseTwo_Committed;
}
switch (branchStatus) {
case PhaseTwo_Committed:
SessionHelper.removeBranch(globalSession, branchSession, !retrying);
LOGGER.info("Commit branch transaction successfully, xid = {} branchId = {}", globalSession.getXid(), branchSession.getBranchId());
return CONTINUE;
case PhaseTwo_CommitFailed_Unretryable:
//not at branch
SessionHelper.endCommitFailed(globalSession, retrying);
LOGGER.error("Committing global transaction[{}] finally failed, caused by branch transaction[{}] commit failed.", globalSession.getXid(), branchSession.getBranchId());
return false;
default:
if (!retrying) {//当前是否重试,
globalSession.queueToRetryCommit();
return false;
}
if (globalSession.canBeCommittedAsync()) {
LOGGER.error("Committing branch transaction[{}], status:{} and will retry later",
branchSession.getBranchId(), branchStatus);
return CONTINUE;
} else {
LOGGER.error(
"Committing global transaction[{}] failed, caused by branch transaction[{}] commit failed, will retry later.", globalSession.getXid(), branchSession.getBranchId());
return false;
}
}
} catch (Exception ex) {
String commitInfo = retrying ? "Global commit continue" : "Global commit failed";
StackTraceLogger.error(LOGGER, ex, "Committing branch transaction exception:retrying={}, {}, {}",
new String[] {String.valueOf(retrying), branchSession.toString(), commitInfo});
if (!retrying) {
globalSession.queueToRetryCommit();
throw new TransactionException(ex);
}
}
return CONTINUE;
}, PARALLEL_HANDLE_BRANCH && branchSessions.size() >= 2);
// Return if the result is not null
if (result != null) {
return result;
}
//If has branch and not all remaining branches can be committed asynchronously,
//do print log and return false 有分支事务,且不能异步提交,说明失败了
if (globalSession.hasBranch() && !globalSession.canBeCommittedAsync()) {
LOGGER.info("Committing global transaction is NOT done, xid = {}.", globalSession.getXid());
return false;
}
}
// if it succeeds and there is no branch, retrying=true is the asynchronous state when retrying. EndCommitted is
// executed to improve concurrency performance, and the global transaction ends.. 分支事务全部提交成功了
if (success && globalSession.getBranchSessions().isEmpty()) {
SessionHelper.endCommitted(globalSession, retrying);//删除全局事务信息
LOGGER.info("Committing global transaction is successfully done, xid = {}.", globalSession.getXid());
}
return success;
}
全局事务的提交,会遍历所有与之关联的分支事务ID,向RM发送BranchCommitRequest 消息,RM分支事务提交成功后将分支事务从全局事务中删除,删除seata库分支事务表branch_table的数据。当所有分支事务全部执行成功后,将GlobalSession信息从数据库中global_table删除
下面看下里面的关键步骤:
1 TC通知RM
java
public BranchStatus branchCommit(GlobalSession globalSession, BranchSession branchSession) throws TransactionException {
try {
BranchCommitRequest request = new BranchCommitRequest();
request.setXid(branchSession.getXid());
request.setBranchId(branchSession.getBranchId());
request.setResourceId(branchSession.getResourceId());
request.setApplicationData(branchSession.getApplicationData());
request.setBranchType(branchSession.getBranchType());
return branchCommitSend(request, globalSession, branchSession);
} catch (IOException | TimeoutException e) {
throw new BranchTransactionException(FailedToSendBranchCommitRequest,
String.format("Send branch commit failed, xid = %s branchId = %s", branchSession.getXid(),
branchSession.getBranchId()), e);
}
}
2 TC 分支事务从全局事务中删除,删除seata库分支事务表branch_table的数据。
java
public static void removeBranch(GlobalSession globalSession, BranchSession branchSession, boolean isAsync)
throws TransactionException {
globalSession.unlockBranch(branchSession);
if (isEnableBranchRemoveAsync() && isAsync) {
COORDINATOR.doBranchRemoveAsync(globalSession, branchSession);
} else {
globalSession.removeBranch(branchSession);
}
}
其中异步删除调用io.seata.server.coordinator.DefaultCoordinator.BranchRemoveTask 来删
,会删除全局锁。
RM提交分支事务
TC发送BranchCommitRequest消息后,RM接收到并将消息交由RmBranchCommitProcessor处理
再io.seata.core.rpc.netty.RmNettyRemotingClient#registerProcessor 注册了
java
private void registerProcessor() {
// 1.registry rm client handle branch commit processor
RmBranchCommitProcessor rmBranchCommitProcessor = new RmBranchCommitProcessor(getTransactionMessageHandler(), this);
super.registerProcessor(MessageType.TYPE_BRANCH_COMMIT, rmBranchCommitProcessor, messageExecutor);
// 2.registry rm client handle branch rollback processor
RmBranchRollbackProcessor rmBranchRollbackProcessor = new RmBranchRollbackProcessor(getTransactionMessageHandler(), this);
super.registerProcessor(MessageType.TYPE_BRANCH_ROLLBACK, rmBranchRollbackProcessor, messageExecutor);
// 3.registry rm handler undo log processor
RmUndoLogProcessor rmUndoLogProcessor = new RmUndoLogProcessor(getTransactionMessageHandler());
super.registerProcessor(MessageType.TYPE_RM_DELETE_UNDOLOG, rmUndoLogProcessor, messageExecutor);
// 4.registry TC response processor
ClientOnResponseProcessor onResponseProcessor =
new ClientOnResponseProcessor(mergeMsgMap, super.getFutures(), getTransactionMessageHandler());
super.registerProcessor(MessageType.TYPE_SEATA_MERGE_RESULT, onResponseProcessor, null);
super.registerProcessor(MessageType.TYPE_BRANCH_REGISTER_RESULT, onResponseProcessor, null);
super.registerProcessor(MessageType.TYPE_BRANCH_STATUS_REPORT_RESULT, onResponseProcessor, null);
super.registerProcessor(MessageType.TYPE_GLOBAL_LOCK_QUERY_RESULT, onResponseProcessor, null);
super.registerProcessor(MessageType.TYPE_REG_RM_RESULT, onResponseProcessor, null);
super.registerProcessor(MessageType.TYPE_BATCH_RESULT_MSG, onResponseProcessor, null);
// 5.registry heartbeat message processor
ClientHeartbeatProcessor clientHeartbeatProcessor = new ClientHeartbeatProcessor();
super.registerProcessor(MessageType.TYPE_HEARTBEAT_MSG, clientHeartbeatProcessor, null);
}
io.seata.core.rpc.processor.client.RmBranchCommitProcessor#process
io.seata.core.rpc.processor.client.RmBranchCommitProcessor#handleBranchCommit
io.seata.rm.AbstractRMHandler#onRequest
io.seata.rm.AbstractRMHandler#handle(io.seata.core.protocol.transaction.BranchCommitRequest)
java
protected void doBranchCommit(BranchCommitRequest request, BranchCommitResponse response)
throws TransactionException {
String xid = request.getXid();
long branchId = request.getBranchId();
String resourceId = request.getResourceId();
String applicationData = request.getApplicationData();
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Branch committing: " + xid + " " + branchId + " " + resourceId + " " + applicationData);
}//真正的处理
BranchStatus status = getResourceManager().branchCommit(request.getBranchType(), xid, branchId, resourceId,
applicationData);
response.setXid(xid);
response.setBranchId(branchId);
response.setBranchStatus(status);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Branch commit result: " + status);
}
io.seata.rm.datasource.DataSourceManager#branchCommit
java
public BranchStatus branchCommit(BranchType branchType, String xid, long branchId, String resourceId,
String applicationData) throws TransactionException {
return asyncWorker.branchCommit(xid, branchId, resourceId);
}
io.seata.rm.datasource.AsyncWorker#branchCommit
java
public BranchStatus branchCommit(String xid, long branchId, String resourceId) {
Phase2Context context = new Phase2Context(xid, branchId, resourceId);
addToCommitQueue(context);//加入提交队列
return BranchStatus.PhaseTwo_Committed;
}
我们看下AsyncWorker的构造方法,也是有任务队列,每1秒执行
java
public AsyncWorker(DataSourceManager dataSourceManager) {
this.dataSourceManager = dataSourceManager;
LOGGER.info("Async Commit Buffer Limit: {}", ASYNC_COMMIT_BUFFER_LIMIT);
commitQueue = new LinkedBlockingQueue<>(ASYNC_COMMIT_BUFFER_LIMIT);
ThreadFactory threadFactory = new NamedThreadFactory("AsyncWorker", 2, true);
scheduledExecutor = new ScheduledThreadPoolExecutor(2, threadFactory);
scheduledExecutor.scheduleAtFixedRate(this::doBranchCommitSafely, 10, 1000, TimeUnit.MILLISECONDS);
}
java
private void doBranchCommit() {
if (commitQueue.isEmpty()) {
return;
}
// transfer all context currently received to this list
List<Phase2Context> allContexts = new LinkedList<>();
commitQueue.drainTo(allContexts);
// group context by their resourceId
Map<String, List<Phase2Context>> groupedContexts = groupedByResourceId(allContexts);
groupedContexts.forEach(this::dealWithGroupedContexts);
}
java
private void dealWithGroupedContexts(String resourceId, List<Phase2Context> contexts) {
if (StringUtils.isBlank(resourceId)) {
//ConcurrentHashMap required notNull key
LOGGER.warn("resourceId is empty and will skip.");
return;
}
DataSourceProxy dataSourceProxy = dataSourceManager.get(resourceId);
if (dataSourceProxy == null) {
LOGGER.warn("failed to find resource for {} and requeue", resourceId);
addAllToCommitQueue(contexts);
return;
}
Connection conn = null;
try {
conn = dataSourceProxy.getPlainConnection();
UndoLogManager undoLogManager = UndoLogManagerFactory.getUndoLogManager(dataSourceProxy.getDbType());
// split contexts into several lists, with each list contain no more element than limit size
List<List<Phase2Context>> splitByLimit = Lists.partition(contexts, UNDOLOG_DELETE_LIMIT_SIZE);
for (List<Phase2Context> partition : splitByLimit) {
deleteUndoLog(conn, undoLogManager, partition);//删除undologr日志
}
} catch (SQLException sqlExx) {
addAllToCommitQueue(contexts);
LOGGER.error("failed to get connection for async committing on {} and requeue", resourceId, sqlExx);
} finally {
IOUtil.close(conn);
}
}
小结:
AT 模式下一阶段结束后,TM向TC发起全局事务提交请求。
TC收到后异步删除全局事务、分支事务、释放全局锁,并通知RM删除undo_log
先整理到这里,回滚的待后续整理。