Seata尝试及源码解读

官网

https://seata.io/zh-cn/docs/overview/what-is-seata.html

seata建表语句
https://gitee.com/dhb414/seata/blob/master/script/server/db/mysql.sql

undo_log https://gitee.com/dhb414/seata/blob/master/script/client/at/db/mysql.sql

配置:

#阿里分布式事务配置
seata:
  service:
    vgroup-mapping:
      #这里的组名my_test_tx_group就是上面已经配置过的
      # seata-server 对应的就是register.conf里的application选项的内容
      my_test_tx_group1: seata-server
    grouplist:
      #这里对应的就是上面的seata-server,后面的蚕食seata服务的IP地址和端口号
      seata-server: l27.0.0.1:9091
    enable-degrade: false
    disable-global-transaction: false

Demo模型

排坑

mysql-connector-java不能用5.1.47,这个版本有bug,以前不会触发,使用seata后会触发

undo_log滞留的问题

AT模式自测用例

 

异步调用的4种情况:

1、主事务调用分支事务之前;2、主事务和分支事务均未结束;3、分支事务结束而主事务没结束;4、主事务结束而分支事务没结束;

异步调用出问题的两种情况:

1、主事务成功,分支事务失败;

2、主事务失败,分支事务成功;

一TM一RM,Feign方式(同步)

同步调用的3个阶段:

1、主事务调用分支事务之前;

2、分支事务结束并返回之前;

3、分支事务提交后,且主事务提交前

同步调用出问题的情况:

1、主事务成功,分支事务失败

2、主事务失败,分支事务成功

正常执行

观察每个阶段的undo_log和TC的记录

阶段一:只有TC全局事务注册

阶段二:只有TC的全局事务表有记录

阶段二(主事务没有使用@Transactional(rollbackFor = Exception.class)):主事务提交,主事务undo存在;分支事务啥都没有;TC全局事务有,锁了主事务,分支事务表注册了主事务

阶段二附加:接阶段二,分支一直断点,主事务会超时而失败,此时TC的全局事务也没有了。等到分支断点放开,分支提交也会因为没有全局事务而失败

阶段二附加(主事务没有使用@Transactional(rollbackFor = Exception.class)):主事务超时失败了,并且发生回滚,全局事务也会回滚,但此时分支事务处于断点,放开断点后分支事务提交时,发现没有全局事务,分支事务也会回滚

阶段三:主事务已提交有undolog;分支事务已提交有undolog;TC全局事务一个,全局锁两个,分支事务注册两个

观察最终结果是否正确

全部符合预期

 

主事务成功,分支事务失败(主事务捕获掉异常)

观察每个阶段的undo_log和TC的记录

观察最终效果是否正确

seata认为,主事务捕获异常代表不关心分支事务,所以主事务正常提交

分支事务由于发生异常,没有正常提交

主事务失败,分支事务成功

观察每个阶段的undo_log和TC的记录

处于阶段三时:跟正常执行的阶段三相同

观察最终效果是否正确

所有分支均回滚

 

源码解读

Seata 中有三大模块,分别是 TM、RM 和 TC。其中 TM 和 RM 是作为 Seata 的客户端与业务系统集成在一起,TC 作为 Seata 的服务端独立部署。

角色划分:
TM: 事务管理,开启、提交、回滚分布式事务
RM: 资源管理,注册、汇报、执资源,负责接收TC发过来的提交、回滚消息,并作出提交,回滚操作
TC: 事务管理器服务功能,存储事务日志、补偿异常事务等、集中管理事务全局锁(全局行锁)


事务执行整体流程:
• TM 开启分布式事务(TM 向 TC 注册全局事务记录);
• 按业务场景,编排数据库、服务等事务内资源(RM 向 TC 汇报资源准备状态 );
• TM 结束分布式事务,事务一阶段结束(TM 通知 TC 提交/回滚分布式事务);
• TC 汇总事务信息,决定分布式事务是提交还是回滚;
• TC 通知所有 RM 提交/回滚 资源,事务二阶段结束;

1、springboot-starter 启动 

SeataAutoConfiguration

1 @Bean
2 @DependsOn({"springUtils"})
3 @ConditionalOnMissingBean(GlobalTransactionScanner.class) //在容器加载它作用的bean时,检查容器中是否存在目标类型的bean,如果存在这跳过原始bean的BeanDefinition加载动作。
4 public GlobalTransactionScanner globalTransactionScanner() {
5 if (LOGGER.isInfoEnabled()) {
6 LOGGER.info("Automatically configure Seata");
7 }
8 return new GlobalTransactionScanner(seataProperties.getApplicationId(), seataProperties.getTxServiceGroup());
9 }

2、初始化

1、、通过Spring的InitializingBean初始化

 1 public class GlobalTransactionScanner extends AbstractAutoProxyCreator
 2     implements InitializingBean, ApplicationContextAware,
 3     DisposableBean{
 4 
 5 //InitializingBean实现方法,spring自动调用
 6 @Override
 7 public void afterPropertiesSet() {
 8 if (disableGlobalTransaction) {
 9 return;
10 }
11 //初始化
12 initClient();
13 }
14 
15 private void initClient() {
16 //init TM register TM success
17 TMClient.init(applicationId, txServiceGroup);
18 //init RM register RM success
19 RMClient.init(applicationId, txServiceGroup);
20 //注册钩子事件,封装销毁操作
21 registerSpringShutdownHook();
22 }
23 }

2.1 Rm netty Channel 启动

1)启动ScheduledExecutorService定时执行器,每5秒尝试进行一次重连TC

2)重连时,先从file.conf中根据分组名称(service_group)找到集群名称(cluster_name)

3)再根据集群名称找到fescar-server集群ip端口列表

4)从ip列表中选择一个用netty进行连接

 

public abstract class AbstractRpcRemotingClient extends AbstractRpcRemoting
    implements RegisterMsgListener, ClientMessageSender {
    @Override
    public void init() {
        clientBootstrap.start();
        //启动ScheduledExecutorService定时执行器,每5秒尝试进行一次重连TC
        timerExecutor.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                clientChannelManager.reconnect(getTransactionServiceGroup());
            }
        }, SCHEDULE_INTERVAL_MILLS, SCHEDULE_INTERVAL_MILLS, TimeUnit.SECONDS);
        if (NettyClientConfig.isEnableClientBatchSendRequest()) {
        //用于多数据合并,减少通信次数
            mergeSendExecutorService = new ThreadPoolExecutor(MAX_MERGE_SEND_THREAD,
                MAX_MERGE_SEND_THREAD,
                KEEP_ALIVE_TIME, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(),
                new NamedThreadFactory(getThreadPrefix(), MAX_MERGE_SEND_THREAD));
            mergeSendExecutorService.submit(new MergedSendRunnable());
        }
        super.init();
    }
    }

2.2 Tm netty Channel 启动

 1 public class RMClient {
 2 
 3     public static void init(String applicationId, String transactionServiceGroup) {
 4         RmRpcClient rmRpcClient = RmRpcClient.getInstance(applicationId, transactionServiceGroup);
 5         //资源管理器ResourceManager
 6         rmRpcClient.setResourceManager(DefaultResourceManager.get());
 7         //消息回调监听器,rmHandler用于接收TC在二阶段发出的提交或者回滚请求
 8         rmRpcClient.setClientMessageListener(new RmMessageListener(DefaultRMHandler.get()));
 9         rmRpcClient.init();
10     }
11 }

注:此处用到了Java Spi拓展机制,可插拔
1
public class DefaultResourceManager implements ResourceManager { 2 protected void initResourceManagers() { 3 //init all resource managers 4 List<ResourceManager> allResourceManagers = EnhancedServiceLoader.loadAll(ResourceManager.class); 5 if (CollectionUtils.isNotEmpty(allResourceManagers)) { 6 for (ResourceManager rm : allResourceManagers) { 7 resourceManagers.put(rm.getBranchType(), rm); 8 } 9 } 10 } 11 }

2.3 

根据注解开启 aop切面

根据@GlobalTransactional注释的方法,通过GlobalTransactionalInterceptor过滤器加入cglib切面,并new TransactionalTemplate开启事务

 1 //BeanPostProcessor后置处理器
 2     @Override
 3     public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
 4         if (bean instanceof DataSource && !(bean instanceof DataSourceProxy) && ConfigurationFactory.getInstance().getBoolean(DATASOURCE_AUTOPROXY, false)) {
 5             DataSourceProxy dataSourceProxy = DataSourceProxyHolder.get().putDataSource((DataSource) bean);
 6             Class<?>[] interfaces = SpringProxyUtils.getAllInterfaces(bean);
 7             return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), interfaces, new InvocationHandler() {
 8                 @Override
 9                 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
10                     Method m = BeanUtils.findDeclaredMethod(DataSourceProxy.class, method.getName(), method.getParameterTypes());
11                     if (null != m) {
12                         return m.invoke(dataSourceProxy, args);
13                     } else {
14                         boolean oldAccessible = method.isAccessible();
15                         try {
16                             method.setAccessible(true);
17                             return method.invoke(bean, args);
18                         } finally {
19                             //recover the original accessible for security reason
20                             method.setAccessible(oldAccessible);
21                         }
22                     }
23                 }
24             });
25         }
26         return super.postProcessAfterInitialization(bean, beanName);
27     }

3、事务一阶段

3.1拦截器

 1 public class GlobalTransactionalInterceptor implements ConfigurationChangeListener,MethodInterceptor {
 2        
 3     @Override
 4     public Object invoke(final MethodInvocation methodInvocation) throws Throwable {
 5         Class<?> targetClass = methodInvocation.getThis() != null ? AopUtils.getTargetClass(methodInvocation.getThis())
 6             : null;
 7         Method specificMethod = ClassUtils.getMostSpecificMethod(methodInvocation.getMethod(), targetClass);
 8         final Method method = BridgeMethodResolver.findBridgedMethod(specificMethod);
 9 
10         final GlobalTransactional globalTransactionalAnnotation = getAnnotation(method, GlobalTransactional.class);
11         final GlobalLock globalLockAnnotation = getAnnotation(method, GlobalLock.class);
12         if (!disable && globalTransactionalAnnotation != null) {
13             //全局事务开始
14             return handleGlobalTransaction(methodInvocation, globalTransactionalAnnotation);
15         } else if (!disable && globalLockAnnotation != null) {
16             //全局锁
17             return handleGlobalLock(methodInvocation);
18         } else {
19             return methodInvocation.proceed();
20         }
21     }

3.2开始事务

TransactionalTemplate

 1 public Object execute(TransactionalExecutor business) throws Throwable {
 2 
 3 // 1. 根据xid判断是否新建事务
 4 GlobalTransaction tx = GlobalTransactionContext.getCurrentOrCreate();
 5 
 6 // 1.1 get transactionInfo
 7 TransactionInfo txInfo = business.getTransactionInfo();
 8 if (txInfo == null) {
 9 throw new ShouldNeverHappenException("transactionInfo does not exist");
10 }
11 try {
12 
13 // 2. begin transaction
14 try {
15 //暂时无用
16 triggerBeforeBegin();
17 //开启事务
18 tx.begin(txInfo.getTimeOut(), txInfo.getName());
19 //暂时无用
20 triggerAfterBegin();
21 } catch (TransactionException txe) {
22 throw new TransactionalExecutor.ExecutionException(tx, txe,
23 TransactionalExecutor.Code.BeginFailure);
24 
25 }
26 
27 Object rs = null;
28 try {
29 
30 // Do Your Business
31 rs = business.execute();
32 
33 } catch (Throwable ex) {
34 
35 // 3.the needed business exception to rollback.
36 completeTransactionAfterThrowing(txInfo,tx,ex);
37 throw ex;
38 }
39 
40 // 4. everything is fine, commit.
41 commitTransaction(tx);
42 
43 return rs;
44 } finally {
45 //5. clear
46 triggerAfterCompletion();
47 cleanUp();
48 }
49 }

真正执行事务开始的地方

 1 public class DefaultGlobalTransaction implements GlobalTransaction {
 2     
 3     @Override
 4     public void begin(int timeout, String name) throws TransactionException {
 5         //此处的角色判断有关键的作用
 6         //表明当前是全局事务的发起者(Launcher)还是参与者(Participant)
 7         //如果在分布式事务的下游系统方法中也加上GlobalTransactional注解
 8         //那么它的角色就是Participant,即会忽略后面的begin就退出了
 9         //而判断是发起者(Launcher)还是参与者(Participant)是根据当前上下文是否已存在XID来判断
10         //没有XID的就是Launcher,已经存在XID的就是Participant
11         if (role != GlobalTransactionRole.Launcher) {
12             check();
13             if (LOGGER.isDebugEnabled()) {
14                 LOGGER.debug("Ignore Begin(): just involved in global transaction [" + xid + "]");
15             }
16             return;
17         }
18         if (xid != null) {
19             throw new IllegalStateException();
20         }
21         if (RootContext.getXID() != null) {
22             throw new IllegalStateException();
23         }
24         //具体开启事务的方法,获取TC返回的XID,具体由DefaultTransactionManager操作
25         xid = transactionManager.begin(null, null, name, timeout);
26         status = GlobalStatus.Begin;
27         RootContext.bind(xid);
28         if (LOGGER.isDebugEnabled()) {
29             LOGGER.debug("Begin a NEW global transaction [" + xid + "]");
30         }
31     }
32 }

3.3 数据源代理

  1 public class StatementProxy<T extends Statement> extends AbstractStatementProxy<T> {
  2 
  3         @Override
  4     public boolean execute(String sql) throws SQLException {
  5         this.targetSQL = sql;
  6         return ExecuteTemplate.execute(this, new StatementCallback<Boolean, T>() {
  7             @Override
  8             public Boolean execute(T statement, Object... args) throws SQLException {
  9                 return statement.execute((String) args[0]);
 10             }
 11         }, sql);
 12     }
 13 }
 14 
 15 public class ExecuteTemplate{
 16     
 17    public static <T, S extends Statement> T execute(SQLRecognizer sqlRecognizer,
 18                                                      StatementProxy<S> statementProxy,
 19                                                      StatementCallback<T, S> statementCallback,
 20                                                      Object... args) throws SQLException {
 21         if (!RootContext.inGlobalTransaction() && !RootContext.requireGlobalLock()) {
 22         // 未开启全局事务时,正常执行
 23             return statementCallback.execute(statementProxy.getTargetStatement(), args);
 24         }
 25         //解析SQL
 26         if (sqlRecognizer == null) {
 27             sqlRecognizer = SQLVisitorFactory.get(
 28                     statementProxy.getTargetSQL(),
 29                     statementProxy.getConnectionProxy().getDbType());
 30         }
 31         Executor<T> executor = null;
 32         if (sqlRecognizer == null) {
 33             executor = new PlainExecutor<T, S>(statementProxy, statementCallback);
 34         } else {
 35             //对不同的SQL类型特殊处理
 36             switch (sqlRecognizer.getSQLType()) {
 37                 case INSERT:
 38                     executor = new InsertExecutor<T, S>(statementProxy, statementCallback, sqlRecognizer);
 39                     break;
 40                 case UPDATE:
 41                     executor = new UpdateExecutor<T, S>(statementProxy, statementCallback, sqlRecognizer);
 42                     break;
 43                 case DELETE:
 44                     executor = new DeleteExecutor<T, S>(statementProxy, statementCallback, sqlRecognizer);
 45                     break;
 46                 case SELECT_FOR_UPDATE:
 47                     executor = new SelectForUpdateExecutor<T, S>(statementProxy, statementCallback, sqlRecognizer);
 48                     break;
 49                 default:
 50                     executor = new PlainExecutor<T, S>(statementProxy, statementCallback);
 51                     break;
 52             }
 53         }
 54         T rs = null;
 55         try {
 56             //真正执行业务逻辑
 57             rs = executor.execute(args);
 58         } catch (Throwable ex) {
 59             if (!(ex instanceof SQLException)) {
 60                 // Turn other exception into SQLException
 61                 ex = new SQLException(ex);
 62             }
 63             throw (SQLException)ex;
 64         }
 65         return rs;
 66     }
 67 }
 68 
 69 
 70 public abstract class AbstractDMLBaseExecutor<T, S extends Statement> extends BaseTransactionalExecutor<T, S> {
 71 
 72     //接下来执行到这里
 73       @Override
 74     public T doExecute(Object... args) throws Throwable {
 75         AbstractConnectionProxy connectionProxy = statementProxy.getConnectionProxy();
 76         if (connectionProxy.getAutoCommit()) {
 77             return executeAutoCommitTrue(args);
 78         } else {
 79             return executeAutoCommitFalse(args);
 80         }
 81     }
 82 
 83     protected T executeAutoCommitFalse(Object[] args) throws Exception {
 84         //业务SQL执行前快照
 85         TableRecords beforeImage = beforeImage();
 86         //真正执行业务SQL
 87         T result = statementCallback.execute(statementProxy.getTargetStatement(), args);
 88         //业务SQL执行后快照
 89         TableRecords afterImage = afterImage(beforeImage);
 90         //准备快照
 91         prepareUndoLog(beforeImage, afterImage);
 92         return result;
 93     }
 94     
 95     
 96     protected void prepareUndoLog(TableRecords beforeImage, TableRecords afterImage) throws SQLException {
 97         if (beforeImage.getRows().size() == 0 && afterImage.getRows().size() == 0) {
 98             return;
 99         }
100         ConnectionProxy connectionProxy = statementProxy.getConnectionProxy();
101         TableRecords lockKeyRecords = sqlRecognizer.getSQLType() == SQLType.DELETE ? beforeImage : afterImage;
102         String lockKeys = buildLockKey(lockKeyRecords);
103         connectionProxy.appendLockKey(lockKeys);
104         SQLUndoLog sqlUndoLog = buildUndoItem(beforeImage, afterImage);
105         connectionProxy.appendUndoLog(sqlUndoLog);
106     }
107 }

 

3.4 分支事务注册与事务提交

 1 public class ConnectionProxy extends AbstractConnectionProxy {
 2     
 3     private void doCommit() throws SQLException {
 4         //全局事务提交
 5         if (context.inGlobalTransaction()) {
 6             processGlobalTransactionCommit();
 7         //全局锁提交
 8         } else if (context.isGlobalLockRequire()) {
 9             processLocalCommitWithGlobalLocks();
10         //正常提交
11         } else {
12             targetConnection.commit();
13         }
14     }
15     
16         //全局事务提交
17     private void processGlobalTransactionCommit() throws SQLException {
18         try {
19             //注册branchId,并保存到上下文
20             register();
21         } catch (TransactionException e) {
22             recognizeLockKeyConflictException(e, context.buildLockKeys());
23         }
24         try {
25             if (context.hasUndoLog()) {
26                 //如果包含undolog,则将之前绑定到上下文中的undolog进行入库
27                 UndoLogManagerFactory.getUndoLogManager(this.getDbType()).flushUndoLogs(this);
28             }
29             //本地事务提交
30             targetConnection.commit();
31         } catch (Throwable ex) {
32             LOGGER.error("process connectionProxy commit error: {}", ex.getMessage(), ex);
33             //通过RMClient汇报TC结果
34             report(false);
35             throw new SQLException(ex);
36         }
37         if (IS_REPORT_SUCCESS_ENABLE) {
38             //通过RmRpcClient汇报TC结果
39             report(true);
40         }
41         context.reset();
42     }
43     //注册branchId,并保存到上下文
44     private void register() throws TransactionException {
45         Long branchId = DefaultResourceManager.get().branchRegister(BranchType.AT, getDataSourceProxy().getResourceId(),
46         null, context.getXid(), null, context.buildLockKeys());
47         context.setBranchId(branchId);
48     }
49 }

 

至此一阶段事务完成

4、事务二阶段

在RMClient初始化时,启动了RMHandlerAT接收TC在二阶段发出的提交或者回滚请求

 1 public abstract class AbstractRMHandler extends AbstractExceptionHandler
 2     implements RMInboundHandler, TransactionMessageHandler {
 3 
 4     private static final Logger LOGGER = LoggerFactory.getLogger(AbstractRMHandler.class);
 5 
 6     @Override
 7     public BranchCommitResponse handle(BranchCommitRequest request) {
 8         BranchCommitResponse response = new BranchCommitResponse();
 9         exceptionHandleTemplate(new AbstractCallback<BranchCommitRequest, BranchCommitResponse>() {
10             @Override
11             public void execute(BranchCommitRequest request, BranchCommitResponse response)
12                 throws TransactionException {
13                 doBranchCommit(request, response);
14             }
15         }, request, response);
16         return response;
17     }
18 
19     @Override
20     public BranchRollbackResponse handle(BranchRollbackRequest request) {
21         BranchRollbackResponse response = new BranchRollbackResponse();
22         exceptionHandleTemplate(new AbstractCallback<BranchRollbackRequest, BranchRollbackResponse>() {
23             @Override
24             public void execute(BranchRollbackRequest request, BranchRollbackResponse response)
25                 throws TransactionException {
26                 doBranchRollback(request, response);
27             }
28         }, request, response);
29         return response;
30     }
31 }

全局提交

 交时,RM只需删除Undo_log表

  1 //AT模式下,最终是由AsyncWorker执行提交
  2 public class AsyncWorker implements ResourceManagerInbound { 
  3 @Override
  4     public BranchStatus branchCommit(BranchType branchType, String xid, long branchId, String resourceId,
  5                                      String applicationData) throws TransactionException {
  6         //加入BlockingQueue
  7         if (!ASYNC_COMMIT_BUFFER.offer(new Phase2Context(branchType, xid, branchId, resourceId, applicationData))) {
  8             LOGGER.warn("Async commit buffer is FULL. Rejected branch [" + branchId + "/" + xid
  9                 + "] will be handled by housekeeping later.");
 10         }
 11         return BranchStatus.PhaseTwo_Committed;
 12     }
 13 
 14     public synchronized void init() {
 15         LOGGER.info("Async Commit Buffer Limit: " + ASYNC_COMMIT_BUFFER_LIMIT);
 16         timerExecutor = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("AsyncWorker", 1, true));
 17         //每秒执行
 18         timerExecutor.scheduleAtFixedRate(new Runnable() {
 19             @Override
 20             public void run() {
 21                 try {
 22                     //提交
 23                     doBranchCommits();
 24                 } catch (Throwable e) {
 25                     LOGGER.info("Failed at async committing ... " + e.getMessage());
 26 
 27                 }
 28             }
 29         }, 10, 1000 * 1, TimeUnit.MILLISECONDS);
 30     }
 31 
 32     private void doBranchCommits() {
 33         if (ASYNC_COMMIT_BUFFER.size() == 0) {
 34             return;
 35         }
 36         Map<String, List<Phase2Context>> mappedContexts = new HashMap<>(DEFAULT_RESOURCE_SIZE);
 37         while (!ASYNC_COMMIT_BUFFER.isEmpty()) {
 38             Phase2Context commitContext = ASYNC_COMMIT_BUFFER.poll();
 39             List<Phase2Context> contextsGroupedByResourceId = mappedContexts.get(commitContext.resourceId);
 40            //根据resourceId分组
 41             if (contextsGroupedByResourceId == null) {
 42                 contextsGroupedByResourceId = new ArrayList<>();
 43                 mappedContexts.put(commitContext.resourceId, contextsGroupedByResourceId);
 44             }
 45             contextsGroupedByResourceId.add(commitContext);
 46         }
 47         for (Map.Entry<String, List<Phase2Context>> entry : mappedContexts.entrySet()) {
 48             Connection conn = null;
 49             DataSourceProxy dataSourceProxy;
 50             try {
 51                 try {
 52                     DataSourceManager resourceManager = (DataSourceManager)DefaultResourceManager.get()
 53                         .getResourceManager(BranchType.AT);
 54                     //根据resourceId查找对应dataSourceProxy
 55                     dataSourceProxy = resourceManager.get(entry.getKey());
 56                     if (dataSourceProxy == null) {
 57                         throw new ShouldNeverHappenException("Failed to find resource on " + entry.getKey());
 58                     }
 59                     conn = dataSourceProxy.getPlainConnection();
 60                 } catch (SQLException sqle) {
 61                     LOGGER.warn("Failed to get connection for async committing on " + entry.getKey(), sqle);
 62                     continue;
 63                 }
 64                 List<Phase2Context> contextsGroupedByResourceId = entry.getValue();
 65                 Set<String> xids = new LinkedHashSet<>(UNDOLOG_DELETE_LIMIT_SIZE);
 66                 Set<Long> branchIds = new LinkedHashSet<>(UNDOLOG_DELETE_LIMIT_SIZE);
 67                 for (Phase2Context commitContext : contextsGroupedByResourceId) {
 68                     xids.add(commitContext.xid);
 69                     branchIds.add(commitContext.branchId);
 70                     int maxSize = xids.size() > branchIds.size() ? xids.size() : branchIds.size();
 71                     //1000个一起执行
 72                     if (maxSize == UNDOLOG_DELETE_LIMIT_SIZE) {
 73                         try {
 74                             //删除undo_log
 75                             UndoLogManagerFactory.getUndoLogManager(dataSourceProxy.getDbType()).batchDeleteUndoLog(
 76                                 xids, branchIds, conn);
 77                         } catch (Exception ex) {
 78                             LOGGER.warn("Failed to batch delete undo log [" + branchIds + "/" + xids + "]", ex);
 79                         }
 80                         xids.clear();
 81                         branchIds.clear();
 82                     }
 83                 }
 84                 if (CollectionUtils.isEmpty(xids) || CollectionUtils.isEmpty(branchIds)) {
 85                     return;
 86                 }
 87                 //剩余未满1000的,在执行一次
 88                 try {
 89                     //删除undo_log
 90                     UndoLogManagerFactory.getUndoLogManager(dataSourceProxy.getDbType()).batchDeleteUndoLog(xids,
 91                         branchIds, conn);
 92                 } catch (Exception ex) {
 93                     LOGGER.warn("Failed to batch delete undo log [" + branchIds + "/" + xids + "]", ex);
 94                 }
 95             } finally {
 96                 if (conn != null) {
 97                     try {
 98                         conn.close();
 99                     } catch (SQLException closeEx) {
100                         LOGGER.warn("Failed to close JDBC resource while deleting undo_log ", closeEx);
101                     }
102                 }
103             }
104         }
105     }
106 }

4.2 全局回滚

全局回滚时,根据undo_log回滚

 

public abstract class AbstractUndoLogManager implements UndoLogManager {
    
        protected static final String SELECT_UNDO_LOG_SQL = "SELECT * FROM " + UNDO_LOG_TABLE_NAME + " WHERE "
        + ClientTableColumnsName.UNDO_LOG_BRANCH_XID + " = ? AND " + ClientTableColumnsName.UNDO_LOG_XID
        + " = ? FOR UPDATE";

     @Override
    public void undo(DataSourceProxy dataSourceProxy, String xid, long branchId) throws TransactionException {
        Connection conn = null;
        ResultSet rs = null;
        PreparedStatement selectPST = null;
        boolean originalAutoCommit = true;

        for (; ; ) {
            try {
                conn = dataSourceProxy.getPlainConnection();

                // The entire undo process should run in a local transaction.
                if (originalAutoCommit = conn.getAutoCommit()) {
                    conn.setAutoCommit(false);
                }

                //根据Xid查询出数据
                selectPST = conn.prepareStatement(SELECT_UNDO_LOG_SQL);
                selectPST.setLong(1, branchId);
                selectPST.setString(2, xid);
                rs = selectPST.executeQuery();

                boolean exists = false;
                while (rs.next()) {
                    exists = true;

                    //防重复提交
                    int state = rs.getInt(ClientTableColumnsName.UNDO_LOG_LOG_STATUS);
                    if (!canUndo(state)) {
                        if (LOGGER.isInfoEnabled()) {
                            LOGGER.info("xid {} branch {}, ignore {} undo_log", xid, branchId, state);
                        }
                        return;
                    }

                    String contextString = rs.getString(ClientTableColumnsName.UNDO_LOG_CONTEXT);
                    Map<String, String> context = parseContext(contextString);
                    Blob b = rs.getBlob(ClientTableColumnsName.UNDO_LOG_ROLLBACK_INFO);
                    byte[] rollbackInfo = BlobUtils.blob2Bytes(b);

                    String serializer = context == null ? null : context.get(UndoLogConstants.SERIALIZER_KEY);
                    UndoLogParser parser = serializer == null ? UndoLogParserFactory.getInstance()
                        : UndoLogParserFactory.getInstance(serializer);
                    BranchUndoLog branchUndoLog = parser.decode(rollbackInfo);

                    try {
                        // put serializer name to local
                        setCurrentSerializer(parser.getName());
                        List<SQLUndoLog> sqlUndoLogs = branchUndoLog.getSqlUndoLogs();
                        if (sqlUndoLogs.size() > 1) {
                            Collections.reverse(sqlUndoLogs);
                        }
                        //反解析出回滚SQL并执行
                        for (SQLUndoLog sqlUndoLog : sqlUndoLogs) {
                            TableMeta tableMeta = TableMetaCacheFactory.getTableMetaCache(dataSourceProxy).getTableMeta(
                                conn, sqlUndoLog.getTableName(),dataSourceProxy.getResourceId());
                            sqlUndoLog.setTableMeta(tableMeta);
                            AbstractUndoExecutor undoExecutor = UndoExecutorFactory.getUndoExecutor(
                                dataSourceProxy.getDbType(), sqlUndoLog);
                            undoExecutor.executeOn(conn);
                        }
                    } finally {
                        // remove serializer name
                        removeCurrentSerializer();
                    }
                }
     ....
    
}

知识点:

BeanPostProcessor的作用 https://www.jianshu.com/p/6a48675ef7a3

Bean初始化执行顺序 https://www.cnblogs.com/twelve-eleven/p/8080038.html

JAVA虚拟机关闭钩子 https://www.jianshu.com/p/7de5ee9418f8

JAVA中的SPI机制 https://www.jianshu.com/p/46b42f7f593c

原文地址:https://www.cnblogs.com/anhaogoon/p/13033986.html