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,代碼:

    @Overridepublic boolean isRemoting(Object bean, String beanName) throws FrameworkException {//判斷是否是服務調用方或者是否是服務提供方return isReference(bean, beanName) || isService(bean, beanName);}

2.遠程 bean 解析,把 rpc類 解析成 RemotingDesc,,代碼:

@Overridepublic 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 resourceObject 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 resourceDefaultResourceManager.get().registerResource(tccResource);}}} catch (Throwable t) {throw new FrameworkException(t, "parser remoting service error");}}if (isReference(bean, beanName)) {//reference bean, TCC proxyremotingBeanDesc.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 注冊:

    @Overridepublic 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.分支的提交或者回滾:

    @Overridepublic 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;//BusinessActionContextBusinessActionContext 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 nameString actionName = businessAction.name();BusinessActionContext actionContext = new BusinessActionContext();actionContext.setXid(xid);//set action anmeactionContext.setActionName(actionName);//Creating Branch RecordString branchId = doTccActionLogStore(method, arguments, businessAction, actionContext);actionContext.setBranchId(branchId);//set the parameter whose type is BusinessActionContextClass<?>[] 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 methodret.put(Constants.TCC_METHOD_ARGUMENTS, arguments);//the final resultret.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.pswp.cn/web/40828.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/40828.shtml
英文地址,請注明出處:http://en.pswp.cn/web/40828.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

《夢醒蝶飛:釋放Excel函數與公式的力量》9.2 FV函數

9.2 FV函數 FV函數是Excel中用于計算投資或貸款在若干期后的未來值的函數。它是一個非常實用的財務函數&#xff0c;能夠幫助我們快速計算投資的最終價值或貸款的期末余額。 9.2.1 函數簡介 FV函數用于計算基于定期固定支付和固定利率的投資或貸款的未來值。未來值是指在一定…

cs224n作業3 代碼及運行結果

代碼里要求用pytorch1.0.0版本&#xff0c;其實不用也可以的。 【刪掉run.py里的assert(torch.version “1.0.0”)即可】 代碼里面也有提示讓你實現什么&#xff0c;弄懂代碼什么意思基本就可以了&#xff0c;看多了感覺大框架都大差不差。多看多練慢慢來&#xff0c;加油&am…

中英雙語介紹美國的州:阿肯色州(Arkansas)

中文版 阿肯色州簡介 阿肯色州&#xff08;Arkansas&#xff09;位于美國南部&#xff0c;以其多樣的自然景觀、豐富的文化遺產和不斷發展的經濟而聞名。以下是對阿肯色州的詳細介紹&#xff0c;包括其地理位置、人口、經濟、教育、文化和主要城市。 地理位置 阿肯色州東臨…

文件、文本閱讀與重定向、路徑與理解指令——linux指令學習(一)

前言&#xff1a;本節內容標題雖然為指令&#xff0c;但是并不只是講指令&#xff0c; 更多的是和指令相關的一些原理性的東西。 如果友友只想要查一查某個指令的用法&#xff0c; 很抱歉&#xff0c; 本節不是那種帶有字典性質的文章。但是如果友友是想要來學習的&#xff0c;…

PD虛擬機怎么聯網?PD虛擬機安裝Win11無法上網 pd虛擬機連不上網怎么解決 mac安裝windows虛擬機教程

PD虛擬機既可以聯網使用&#xff0c;也可以單機使用。如需將PD虛擬機聯網&#xff0c;可以共享Mac原生系統的網絡&#xff0c;其使用體驗與真實系統無異。本文會詳細講解PD虛擬機如何聯網&#xff0c;并會進一步解決PD虛擬機安裝Win10無法上網的問題。 如果有網絡相關問題的小伙…

游戲服務器搭建選VPS還是專用服務器?

游戲服務器搭建選VPS&#xff0c;VPS能夠提供控制、性能和穩定性。它不僅僅是讓游戲保持活力。它有助于減少延遲問題&#xff0c;增強您的游戲體驗。 想象一下&#xff1a;你正沉浸在一場游戲中。 勝利在望。突然&#xff0c;屏幕卡住——服務器延遲。 很崩潰&#xff0c;對…

C語言實現【程序設計與實踐】實驗三:自動售貨機

聲明&#xff1a;著作權歸作者所有。商業轉載請聯系作者獲得授權&#xff0c;非商業轉載請注明出處。 附上c版http://t.csdnimg.cn/BbDSL https://blog.csdn.net/As_sBomb/article/details/105485940 實驗三&#xff1a;自動售貨機 題目&#xff1a; 圖所示為簡易自動售貨…

1493. 刪掉一個元素以后全為 1 的最長子數組

1493. 刪掉一個元素以后全為 1 的最長子數組 題目鏈接&#xff1a;1493. 刪掉一個元素以后全為 1 的最長子數組 代碼如下&#xff1a; class Solution { public://滑動窗口int longestSubarray(vector<int>& nums){int res 0;int count 0;//記錄碰到0的個數int …

【MYSQL】事務隔離級別以及InnerDB底層實現

事務隔離級別 讀未提交&#xff08;Read Uncommitted&#xff09; 允許事務讀取其他事務未提交的數據&#xff0c;可能會導致臟讀。 讀已提交&#xff08;Read Committed&#xff09; 一個事務只能看見已經提交的事務所做的更改&#xff0c;可以避免臟讀&#xff0c;但可能…

win7系統快速安裝python

下載安裝包 建議選擇python3.8左右的&#xff0c;我下載的是3.7.8&#xff0c;最新版本的pythonwin7可能不支持 python網址 下拉尋找 安裝python 1.雙擊安裝包 更換完地址選擇安裝(install) 安裝完成后點擊close即可 測試是否安裝成功 1.winr快捷鍵打開黑窗口輸入cmd …

idea創建的maven項目pom文件引入的坐標報紅原因

如下所示 我們在引入某些依賴坐標的時候&#xff0c;即使點擊了右上角的mavne刷新之后還是報紅。 其實這是正常現象&#xff0c;實際上是我們的本地倉庫當中沒有這些依賴坐標&#xff0c;而idea就會通過報紅來標記這些依賴來說明在我們的本地倉庫是不存在的。 那有的同學就會…

【HICE】dns正向解析

1.編輯倉庫 2.掛載 3.下載軟件包 4.編輯named.conf 5.編輯named.haha 6.重啟服務 7.驗證本地域名是否解析

1116. 打印零與奇偶數

題目描述 現有函數 printNumber 可以用一個整數參數調用&#xff0c;并輸出該整數到控制臺。 例如&#xff0c;調用 printNumber(7) 將會輸出 7 到控制臺。 給你類 ZeroEvenOdd 的一個實例&#xff0c;該類中有三個函數&#xff1a;zero、even 和 odd 。ZeroEvenOdd 的相同實…

六、快速啟動框架:SpringBoot3實戰-個人版

六、快速啟動框架&#xff1a;SpringBoot3實戰 文章目錄 六、快速啟動框架&#xff1a;SpringBoot3實戰一、SpringBoot3介紹1.1 SpringBoot3簡介1.2 系統要求1.3 快速入門1.4 入門總結回顧復習 二、SpringBoot3配置文件2.1 統一配置管理概述2.2 屬性配置文件使用2.3 YAML配置文…

短劇app開發搭建需要哪些資質證件?

短劇APP需要辦理的資質包括增值電信業務經營許可證&#xff08;ICP&#xff09;、網絡文化經營許可證&#xff08;文網文&#xff09;、廣播電視節目制作經營許可證&#xff0c;以及軟件著作權&#xff08;軟著&#xff09;。 增值電信業務經營許可證&#xff08;ICP&#xff…

ODOO17的郵件機制-系統自動推送修改密碼的郵件

用戶收到被要求重置密碼的郵件&#xff1a; 我們來分析一下ODOO此郵件的工作機制&#xff1a; 1、郵件模板定義 2、渲染模板的函數&#xff1a; 3、調用此函數的機制&#xff1a; 當用戶移除或增加了信任的設備&#xff08;如電腦、手機端等&#xff09;&#xff0c;系統會自…

Python爬蟲之什么是逆向工程?逆向是什么?

Python爬蟲之什么是逆向工程&#xff1f;逆向是什么&#xff1f; 在Python爬蟲領域&#xff0c;逆向工程是一種重要的技術手段&#xff0c;尤其在面對復雜的網站結構和加密的數據時。逆向工程通常涉及對目標網站的分析&#xff0c;包括其前端代碼、后端邏輯、數據傳輸方式等&am…

CentOS 7.9 停止維護(2024-6-30)后可用在線yum源 —— 筑夢之路

眾所周知&#xff0c;centos 7 在2024年6月30日&#xff0c;生命周期結束&#xff0c;官方不再進行支持維護&#xff0c;而很多環境一時之間無法完全更新替換操作系統&#xff0c;因此對于yum源還是需要的&#xff0c;特別是對于互聯網環境來說&#xff0c;在線yum源使用方便很…

三級_網絡技術_01_網絡系統結構與設計的基本原則

1.下列關于RPR技術的描述中&#xff0c;錯誤的是()。 RPR與FDDI一樣使用雙環結構 在RPR環中&#xff0c;源節點向目的節點成功發出的數據幀要由目的節點從環中收回 RPR環中每一個節點都執行MPLS公平算法 RPR環能夠在50ms內實現自愈 2.下列關于RPR技術的描述中&#xff0c;…

從0到1:培訓老師預約小程序開發筆記二

背景調研 培訓老師預約小程序&#xff1a; 教師和學生可以更便捷地安排課程&#xff0c;并提升教學質量和學習效果&#xff0c;使之成為管理和提升教學效果的強大工具。培訓老師可以在小程序上設置自己的可預約時間&#xff0c;學員可以根據老師的日程安排選擇合適的時間進行預…