Apache Seata tcc 模块源码分析

本文来自 Apache Seata官方文档,欢迎访问官网,查看更多深度文章。
本文来自 Apache Seata官方文档,欢迎访问官网,查看更多深度文章。

一 .导读

spring 模块分析中讲到,Seata 的 spring 模块会对涉及到分布式业务的 bean 进行处理。项目启动时,当 GlobalTransactionalScanner 扫描到 TCC 服务的 reference 时(即tcc事务参与方),会对其进行动态代理,即给 bean 织入 TCC 模式下的 MethodInterceptor 的实现类。tcc 事务发起方依然使用 @GlobalTransactional 注解开启,织入的是通用的 MethodInterceptor 的实现类。

TCC 模式下的 MethodInterceptor 实现类即 TccActionInterceptor(spring模块) ,这个类中调用了 ActionInterceptorHandler(tcc模块) 进行 TCC 模式下事务流程的处理。

TCC 动态代理的主要功能是:生成TCC运行时上下文、透传业务参数、注册分支事务记录。

二 .TCC模式介绍

在2PC(两阶段提交)协议中,事务管理器分两阶段协调资源管理,资源管理器对外提供三个操作,分别是一阶段的准备操作,和二阶段的提交操作和回滚操作。

public interface TccAction {

    @TwoPhaseBusinessAction(name = "tccActionForTest" , commitMethod = "commit", rollbackMethod = "rollback")
    public boolean prepare(BusinessActionContext actionContext,
                           @BusinessActionContextParameter(paramName = "a") int a,
                           @BusinessActionContextParameter(paramName = "b", index = 0) List b,
                           @BusinessActionContextParameter(isParamInProperty = true) TccParam tccParam);

    public boolean commit(BusinessActionContext actionContext);
    
    public boolean rollback(BusinessActionContext actionContext);
}

这是 TCC 参与者实例,参与者需要实现三个方法,第一个参数必须是 BusinessActionContext ,方法返回类型固定,对外发布成微服务,供事务管理器调用。

prepare:资源的检查和预留。例:扣减账户的余额,并增加相同的冻结余额。

commit:使用预留的资源,完成真正的业务操作。例:减少冻结余额,扣减资金业务完成。

cancel:释放预留资源。例:冻结余额加回账户的余额。

其中 BusinessActionContext 封装了本次事务的上下文环境:xid、branchId、actionName 和被 @BusinessActionContextParam 注解的参数等。

参与方业务有几个需要注意的地方:
1.控制业务幂等性,需要支持同一笔事务的重复提交和重复回滚。
2.防悬挂,即二阶段的回滚,比一阶段的 try 先执行。
3.放宽一致性协议,最终一致,所以是读已修改

三 . remoting 包解析

在这里插入图片描述

包中所有的类都是为包中的 DefaultRemotingParser 服务,Dubbo、LocalTCC、SofaRpc 分别负责解析各自RPC协议下的类。

DefaultRemotingParser 的主要方法:
1.判断 bean 是否是 remoting bean,代码:

    @Override
    public boolean isRemoting(Object bean, String beanName) throws FrameworkException {
        //判断是否是服务调用方或者是否是服务提供方
        return isReference(bean, beanName) || isService(bean, beanName);
    }

2.远程 bean 解析,把 rpc类 解析成 RemotingDesc,,代码:

@Override
    public boolean isRemoting(Object bean, String beanName) throws FrameworkException {
        //判断是否是服务调用方或者是否是服务提供方
        return isReference(bean, beanName) || isService(bean, beanName);
    }

利用 allRemotingParsers 来解析远程 bean 。allRemotingParsers是在:initRemotingParser() 中调用EnhancedServiceLoader.loadAll(RemotingParser.class) 动态进行 RemotingParser 子类的加载,即 SPI 加载机制。

如果想扩展,比如实现一个feign远程调用的解析类,只要把RemotingParser相关实现类写在 SPI 的配置中就可以了,扩展性很强。

RemotingDesc 事务流程需要的远程 bean 的一些具体信息,比如 targetBean、interfaceClass、interfaceClassName、protocol、isReference等等。

3.TCC资源注册

public RemotingDesc parserRemotingServiceInfo(Object bean, String beanName) {
        RemotingDesc remotingBeanDesc = getServiceDesc(bean, beanName);
        if (remotingBeanDesc == null) {
            return null;
        }
        remotingServiceMap.put(beanName, remotingBeanDesc);

        Class<?> interfaceClass = remotingBeanDesc.getInterfaceClass();
        Method[] methods = interfaceClass.getMethods();
        if (isService(bean, beanName)) {
            try {
                //service bean, registry resource
                Object targetBean = remotingBeanDesc.getTargetBean();
                for (Method m : methods) {
                    TwoPhaseBusinessAction twoPhaseBusinessAction = m.getAnnotation(TwoPhaseBusinessAction.class);
                    if (twoPhaseBusinessAction != null) {
                        TCCResource tccResource = new TCCResource();
                        tccResource.setActionName(twoPhaseBusinessAction.name());
                        tccResource.setTargetBean(targetBean);
                        tccResource.setPrepareMethod(m);
                        tccResource.setCommitMethodName(twoPhaseBusinessAction.commitMethod());
                        tccResource.setCommitMethod(ReflectionUtil
                            .getMethod(interfaceClass, twoPhaseBusinessAction.commitMethod(),
                                new Class[] {BusinessActionContext.class}));
                        tccResource.setRollbackMethodName(twoPhaseBusinessAction.rollbackMethod());
                        tccResource.setRollbackMethod(ReflectionUtil
                            .getMethod(interfaceClass, twoPhaseBusinessAction.rollbackMethod(),
                                new Class[] {BusinessActionContext.class}));
                        //registry tcc resource
                        DefaultResourceManager.get().registerResource(tccResource);
                    }
                }
            } catch (Throwable t) {
                throw new FrameworkException(t, "parser remoting service error");
            }
        }
        if (isReference(bean, beanName)) {
            //reference bean, TCC proxy
            remotingBeanDesc.setReference(true);
        }
        return remotingBeanDesc;
    }

首先判断是否是事务参与方,如果是,拿到 RemotingDesc 中的 interfaceClass,遍历接口中的方法,判断方法上是否有@TwoParserBusinessAction 注解,如果有,把参数封装成 TCCRecource,通过 DefaultResourceManager 进行 TCC 资源的注册。

这里 DefaultResourceManager 会根据 Resource 的 BranchType 来寻找对应的资源管理器,TCC 模式下资源管理类,在 tcc 模块中。

这个 rpc 解析类主要提供给 spring 模块进行使用。parserRemotingServiceInfo() 被封装到了 spring 模块的 TCCBeanParserUtils 工具类中。spring 模块的 GlobalTransactionScanner 在项目启动的时候,通过工具类解析 TCC bean,工具类 TCCBeanParserUtils 会调用 TCCResourceManager 进行资源的注册,并且如果是全局事务的服务提供者,会织入 TccActionInterceptor 代理。这些个流程是 spring 模块的功能,tcc 模块是提供功能类给 spring 模块使用。

三 .tcc 资源管理器

TCCResourceManager 负责管理 TCC 模式下资源的注册、分支的注册、提交、和回滚。

1.在项目启动时, spring 模块的 GlobalTransactionScanner 扫描到 bean 是 tcc bean 时,会本地缓存资源,并向 server 注册:

    @Override
    public void registerResource(Resource resource) {
        TCCResource tccResource = (TCCResource)resource;
        tccResourceCache.put(tccResource.getResourceId(), tccResource);
        super.registerResource(tccResource);
    }

与server通信的逻辑被封装在了父类 AbstractResourceManage 中,这里根据 resourceId 对 TCCResource 进行缓存。父类 AbstractResourceManage 注册资源的时候,使用 resourceGroupId + actionName,actionName 就是 @TwoParseBusinessAction 注解中的 name,resourceGroupId 默认是 DEFAULT。

2.事务分支的注册在 rm-datasource 包下的 AbstractResourceManager 中,注册时参数 lockKeys 为 null,和 AT 模式下事务分支的注册还是有些不一样的。

3.分支的提交或者回滚:

    @Override
    public BranchStatus branchCommit(BranchType branchType, String xid, long branchId, String resourceId,
                                     String applicationData) throws TransactionException {
        TCCResource tccResource = (TCCResource)tccResourceCache.get(resourceId);
        if (tccResource == null) {
            throw new ShouldNeverHappenException("TCC resource is not exist, resourceId:" + resourceId);
        }
        Object targetTCCBean = tccResource.getTargetBean();
        Method commitMethod = tccResource.getCommitMethod();
        if (targetTCCBean == null || commitMethod == null) {
            throw new ShouldNeverHappenException("TCC resource is not available, resourceId:" + resourceId);
        }
        try {
            boolean result = false;
            //BusinessActionContext
            BusinessActionContext businessActionContext = getBusinessActionContext(xid, branchId, resourceId,
                applicationData);
            Object ret = commitMethod.invoke(targetTCCBean, businessActionContext);
            if (ret != null) {
                if (ret instanceof TwoPhaseResult) {
                    result = ((TwoPhaseResult)ret).isSuccess();
                } else {
                    result = (boolean)ret;
                }
            }
            return result ? BranchStatus.PhaseTwo_Committed : BranchStatus.PhaseTwo_CommitFailed_Retryable;
        } catch (Throwable t) {
            LOGGER.error(msg, t);
            throw new FrameworkException(t, msg);
        }
    }

通过参数 xid、branchId、resourceId、applicationData 恢复业务的上下文 businessActionContext。

根据获取到的上下文通过反射执行 commit 方法,并返回执行结果。回滚方法类似。

这里 branchCommit() 和 branchRollback() 提供给 rm 模块资源处理的抽象类 AbstractRMHandler 调用,这个 handler 是 core 模块定义的模板方法的进一步实现类。和 registerResource() 不一样,后者是 spring 扫描时主动注册资源。

四 . tcc 模式事务处理

spring 模块中的 TccActionInterceptor 的 invoke() 方法在被代理的 rpc bean 被调用时执行。该方法先获取 rpc 拦截器透传过来的全局事务 xid ,然后 TCC 模式下全局事务参与者的事务流程还是交给 tcc 模块 ActionInterceptorHandler 处理。

也就是说,事务参与者,在项目启动的时候,被代理。真实的业务方法,在 ActionInterceptorHandler 中,通过回调执行。

    public Map<String, Object> proceed(Method method, Object[] arguments, String xid, TwoPhaseBusinessAction businessAction,
                                       Callback<Object> targetCallback) throws Throwable {
        Map<String, Object> ret = new HashMap<String, Object>(4);

        //TCC name
        String actionName = businessAction.name();
        BusinessActionContext actionContext = new BusinessActionContext();
        actionContext.setXid(xid);
        //set action anme
        actionContext.setActionName(actionName);

        //Creating Branch Record
        String branchId = doTccActionLogStore(method, arguments, businessAction, actionContext);
        actionContext.setBranchId(branchId);

        //set the parameter whose type is BusinessActionContext
        Class<?>[] types = method.getParameterTypes();
        int argIndex = 0;
        for (Class<?> cls : types) {
            if (cls.getName().equals(BusinessActionContext.class.getName())) {
                arguments[argIndex] = actionContext;
                break;
            }
            argIndex++;
        }
        //the final parameters of the try method
        ret.put(Constants.TCC_METHOD_ARGUMENTS, arguments);
        //the final result
        ret.put(Constants.TCC_METHOD_RESULT, targetCallback.execute());
        return ret;
    }

这里有两个重要操作:

1.doTccActionLogStore() 这个方法中,调用了两个比较重要的方法:
fetchActionRequestContext(method, arguments),这个方法把被 @BusinessActionContextParam 注解的参数取出来,在下面的 init 方法中塞入 BusinessActionComtext ,同时塞入的还有事务相关参数。
DefaultResourceManager.get().branchRegister(BranchType.TCC, actionName, null, xid,applicationContextStr, null),这个方法执行 TCC 模式下事务参与者事务分支的注册。

2.回调执行 targetCallback.execute() ,被代理的 bean 具体的业务,即 prepare() 方法。

五 .总结

tcc模块,主要提供以下功能 :

  1. 定义两阶段协议注解,提供 tcc 模式下事务流程需要的属性。
  2. 提供解析不同 rpc 框架 remoting bean 的 ParserRemoting 实现,供 spring 模块调用。
  3. 提供 TCC 模式下资源管理器,进行资源注册、事务分支注册提交回滚等。
  4. 提供 TCC 模式下事务流程的处理类,让 MethodInterceptor 代理类不执行具体模式的事务流程,而是下放到 tcc 模块。

五 .相关

作者:赵润泽,系列地址。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/779919.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

进程控制-wait和waitpid进程回收

wait 阻塞函数 函数作用&#xff1a; 1. 阻塞并等待子进程退出 2. 回收子进程残留资源 3. 获取子进程结束状态&#xff08;退出原因&#xff09; pid_t wait(int *wstatus); 返回值&#xff1a; ‐1 : 回收失败&#xff0c;已经没有子进程了 >0 : 回收子进程对应的…

《linux系统内核设计与实现》-实现最简单的字符设备驱动

开发linux内核驱动需要以下4个步骤&#xff1a; 1 编写hello驱动代码 驱动代码如下 helloDev.c&#xff0c;这是一个最小、最简单的驱动&#xff0c;去掉了其他的不相干代码&#xff0c;尽量让大家能了解驱动本身。 #include <linux/module.h> #include <linux/mod…

python函数和c的区别有哪些

Python有很多内置函数&#xff08;build in function&#xff09;&#xff0c;不需要写头文件&#xff0c;Python还有很多强大的模块&#xff0c;需要时导入便可。C语言在这一点上远不及Python&#xff0c;大多时候都需要自己手动实现。 C语言中的函数&#xff0c;有着严格的顺…

vulhub-activemq(CVE-2016-3088)

在 Apache ActiveMQ 5.12.x~5.13.x 版本中&#xff0c;默认关闭了 fileserver 这个应用&#xff08;不过&#xff0c;可以在conf/jetty.xml 中开启&#xff09;&#xff1b;在 5.14.0 版本后&#xff0c;彻底删除了 fileserver 应用。【所以在渗透测试过程中要确定好 ActiveMQ …

2024年世界人工智能大会(WAIC)各大佬的精彩发言

2024年世界人工智能大会&#xff08;WAIC&#xff09;在上海举行&#xff0c;受到了广泛关注和参与。以下是大会首日的主要观点和议题的总结&#xff1a; AI 应用落地&#xff1a;大会讨论了AI应用如何落地&#xff0c;即如何在当前阶段利用大模型技术实现实际应用。 AI 安全&…

nginx转发的问题

我在项目配置的时候遇到一个问题&#xff1a; 配置了域名转发&#xff0c;且配置了https nginx配置如下&#xff1a; server {listen 443 ssl;server_name yourdomain.com;ssl_certificate /path/to/your/certificate.crt;ssl_certificate_key /path/to/your/private.key;loca…

收银系统源码-线上商城预售功能

1.功能描述 预售&#xff1a;智慧新零售收银系统&#xff0c;线上商城营销插件之一&#xff0c;商品出售时可设置以支付定金或全款的方式提前预售&#xff0c;门店按订单量备货&#xff0c;降低压货成本&#xff1b; 2.适用场景 易损商品提前下单备货&#xff0c;如水果生鲜…

拼多多20240509实习生笔试

题目一 解题思路 分类讨论 情况一&#xff1a;5元汉堡也买不完。 情况二&#xff1a;5元汉堡能买完&#xff0c;非5元买不起。 情况三&#xff1a;都能买起&#xff0c;或还有剩余买原价汉堡。 题目二 解题思路 找规律&#xff0c;假设有...xy...&#xff0c;x在前。如果交换x…

KubeSphere 社区双周报|2024.06.21-07.04

KubeSphere 社区双周报主要整理展示新增的贡献者名单和证书、新增的讲师证书以及两周内提交过 commit 的贡献者&#xff0c;并对近期重要的 PR 进行解析&#xff0c;同时还包含了线上/线下活动和布道推广等一系列社区动态。 本次双周报涵盖时间为&#xff1a;2024.06.21-07.04…

nodejs实现:支付宝订单查询

nodejs实现&#xff1a;支付宝订单查询&#xff1b; 原生http请求&#xff0c;不使用三方库&#xff1b; 代码如下&#xff1a; const https require(https); const crypto require(crypto); const querystring require(querystring);// 支付宝公共参数 const PRIVATE_KE…

联想小新14Pro,误删了一个注册表,怎么办?

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

flask模块化、封装使用cache(flask_caching)

1.安装flask_caching库 pip install flask_caching 2.创建utils Python 软件包以及cache_helper.py 2.1cache_helper.py代码 from flask_caching import Cachecache Cache()class CacheHelper:def __init__(self, app, config):cache.init_app(app, config)staticmethoddef…

常见的Java运行时异常

常见的Java运行时异常 1、ArithmeticException&#xff08;算术异常&#xff09;2、ClassCastException &#xff08;类转换异常&#xff09;3、IllegalArgumentException &#xff08;非法参数异常&#xff09;4、IndexOutOfBoundsException &#xff08;下标越界异常&#xf…

【python】python母婴数据分析模型预测可视化(数据集+论文+PPT+源码)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

AiPPT的成功之路:PMF付费率与增长策略

如果要给 2023 年的 AI 市场一个关键词&#xff0c;那肯定是“大模型”&#xff0c;聚光灯和大家的注意力、资金都投向了那些大模型公司&#xff1b;而如果要给 2024 年的 AI 市场一个关键词&#xff0c;则一定是 PMF&#xff08;产品市场契合&#xff09;。如果没有 PMF&#…

VuePress 的更多配置

现在&#xff0c;读者应该对 VuePress、主题和插件等有了基本的认识&#xff0c;除了插件&#xff0c;VuePress 自身也有很多有用的配置&#xff0c;这里简单说明下。 ‍ ‍ VuePress 的介绍 在介绍了 VuePress 的基本使用、主题和插件的概念之后&#xff0c;我们再来看看官…

Oracle RAC 19c 打补丁至最新版本-19.23.0.0.0

实验环境-我是从19.0.0.0直接打到19.23.0.0.0&#xff0c;适合刚部署好的集群打补丁直接到最新版本。 查看当前环境 查询集群中运行的 Oracle Clusterware 软件的 activex 版 查询本地节点上二进制文件中存储的 Oracle Clusterware 软件的版本 查询本地服务器上 OHAS 和 Oracle…

windows无法访问github

##一、如果发现windows无法访问github时 一般就是我们的dns出现了问题&#xff0c;此时我们需要更换一个dns访问 ##二、解决方法 首先我们访问ip查询地址&#xff0c; https://ipchaxun.com/github.com/ 可更换下面历史ip进行测试&#xff0c;在windows的cmd里面输入ping git…

【C++】开源:命令行解析库CLI11配置与使用

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍命令行解析库CLI11配置与使用。 无专精则不能成&#xff0c;无涉猎则不能通。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&#x…

苹果清理软件:让你的设备焕然一新

随着时间的推移&#xff0c;无论是Mac电脑还是iOS设备&#xff0c;都可能会因为积累的垃圾文件、缓存、未使用的应用和其他冗余数据而开始表现出性能下降。这不仅会占用宝贵的存储空间&#xff0c;还可能影响设备的响应速度和电池寿命。幸运的是&#xff0c;有多种苹果清理软件…