Seata源码分析——@GlobalTransactional


前言

研读Seata源码有一段时间了,打算出一系列关于源码的文章,旨在加强自己对Seata的理解,同时也希望能帮助到读者。


脑图

本文主要分析标红的地方
在这里插入图片描述


Seata三大角色

TC :事务协调者,netty server
TM :事务管理器,netty client
RM: 资源管理器,netty client

在这里插入图片描述

  1. 只要方法上加了@GlobalTransactional,Seata通过aop检测到之后,就会使用TM和TC通信,注册全局事务。

  2. 在@GlobalTransactional涵括的代码中,不管是本服务中的sql操作,还是feign调用别的服务的sql操作,只要sql操作满足如下:insert操作,delete操作,update操作,select for update操作。 就会被seata增强,使用RM与TC通信,注册分支事务。


@GlobalTransactional

源码入口

在这里插入图片描述
GlobalTransactionScanner继承自AbstractAutoProxyCreator,在这里拦截到加了@GlobalTransactional的方法。

GlobalTransactionScanner

在这里插入图片描述
我们需要关注的方法如下:

  • AbstractAutoProxyCreator:wrapIfNecessary(aop的核心),getAdvicesAndAdvisorsForBean(拦截器)
  • InitializingBean:afterPropertiesSet(初始化TM,RM)

初始化TM,RM

spring生命周期回调接口

@Override
    public void afterPropertiesSet() {
        //是否禁止了全局事务
        if (disableGlobalTransaction) {
            if (LOGGER.isInfoEnabled()) {
                LOGGER.info("Global transaction is disabled.");
            }
            return;
        }
        //初始化netty 客户端(TM  RM)
        initClient();
    }

初始化TM,RM

  private void initClient() {  
        //init TM
        TMClient.init(applicationId, txServiceGroup);
        
        //init RM
        RMClient.init(applicationId, txServiceGroup);
    }

代码最终会调用到RpcClientBootstrap#start
在这里插入图片描述

wrapIfNecessary

在这里插入图片描述
GlobalTransactionalInterceptor#invoke
在这里插入图片描述
GlobalTransactionalInterceptor#handleGlobalTransaction---->TransactionalTemplate#execute
在这里插入图片描述
全局事务的处理是通过spring aop来增强的,下面我们来看看分支事务是如何处理的。


分支事务

代理数据源配置
在这里插入图片描述DataSourceProxy#getConnection

@Override
    public ConnectionProxy getConnection() throws SQLException {
        Connection targetConnection = targetDataSource.getConnection();
        return new ConnectionProxy(this, targetConnection);
    }

ConnectionProxy#doCommit

private void doCommit() throws SQLException {
		//处理@GlobalTransaction的分支事务
        if (context.inGlobalTransaction()) {
            processGlobalTransactionCommit();
        } 
        //处理@GlobalLock,即检查一下是否可以获取全局锁
		else if (context.isGlobalLockRequire()) {
            processLocalCommitWithGlobalLocks();
        } else {
            targetConnection.commit();
        }
    }

ConnectionProxy#processGlobalTransactionCommit

正常情况注册分支事务还会往lock_tabel插入一条记录,代表某个表的某行记录被seata用全局锁锁住了在这里插入图片描述


总结

seata作为初学还是挺难的,问题也很多,比如:

  1. 全局锁怎么实现的?
  2. 为什么需要全局锁?
  3. lock_table什么时候插入记录,什么时候删除?
  4. 读写隔离?

这些问题最好先思考在看答案,读者有更好的问题也可以提在评论区,我会更新博客回答,另外seata.io的快速入门关于AT模式的读写隔离真的绝了,把全局锁讲明白了,建议仔细阅读。

问题回答:

  1. 全局锁使用数据库表实现,lock_table。
  2. 全局锁用于读写隔离,如果有多个分布式事务同时操作同一行数据库记录,那么可以保证数据的正确性。
  3. 注册分支事务的时候会插入lock_table记录(正常情况),全局事务提交的时候会删除lock_table。
  4. 写隔离,如果要用分布式事务,那么对于同一张表更新时建议全使用@GlobalTransaction.
    读隔离,使用@GlobalTransactional+select for update 或者 @GlobalLock+@Transactional+select for update
  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值