文章目錄
- 前言
- 一、@GlobalTransactional
- 1.1、wrapIfNecessary
- 1.2、handleGlobalTransaction
- 1.3、invoke
- 二、總結
前言
??Seata是阿里開源的分布式事務解決方案。在Spring傳統的事務中,開啟事務,執行事務,回滾/提交事務,統一由Spring進行管理,向數據庫發起指令。而分布式事務中,分為了客戶端(微服務端)和服務端(Seata)、注冊中心(通常使用Nacos)這三個部分。Nacos提供了服務注冊與發現,以及管理Seata配置的作用。無論是開啟事務,執行事務,還是提交事務,都不是單個微服務內部自己決定的,而是統一向服務端發起請求,由服務端進行一系列的處理后,再向微服務端發起請求,驅動微服務端執行操作。
一、@GlobalTransactional
??對于微服務端,通常會在分布式事務的入口方法,加上@GlobalTransactional
注解,表示該服務在分布式事務中作為TM的角色。微服務端的源碼解析,自然要從該注解開始。
??在注解的注釋上,標注了三個關鍵方法:
1.1、wrapIfNecessary
??使用Seata,在微服務端需要引入一個依賴:
<!-- seata--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId></dependency>
??通過自動配置機制,可以找到Seata客戶端的核心類:
??在這個類中,裝配了兩個Bean:
FailureHandler
:用于處理事務開始,執行,提交/回滾失敗的情況,留給子類實現,發送郵件或通知告警。默認的實現DefaultFailureHandlerImpl
則是進行日志記錄。GlobalTransactionScanner
:是Seata的全局事務掃描器。
??GlobalTransactionScanner
繼承自AbstractAutoProxyCreator
,重寫了其關鍵方法wrapIfNecessary
:
??在wrapIfNecessary
方法中,首先會判斷當前模式是否為TCC,如果是TCC模式
,創建TccActionInterceptor
攔截器并設置清理任務,同時監聽配置中心的開關DISABLE_GLOBAL_TRANSACTION
。
??否則就是AT模式,會檢查目標類上是否存在@GlobalTransactional
或方法上是否存在@GlobalTransactional
、@GlobalLock
注解,如果存在,使用GlobalTransactionalInterceptor
來代理。
??因為GlobalTransactionalInterceptor
實現了MethodInterceptor
接口,所以將創建出的GlobalTransactionalInterceptor
實例賦值給MethodInterceptor
類型的interceptor
屬性:
??判斷是否存在@GlobalTransactional
、@GlobalLock
注解,這一步非常關鍵,如果判斷不通過,該類就不會做和Seata有關的增強。
existsAnnotation
??如果該Bean沒有被代理過,則調用父類方法創建代理。如果已經是AOP代理,則動態插入Seata的Advisor。
??在super.wrapIfNecessary
中,有一行關鍵代碼getAdvicesAndAdvisorsForBean
,目的是獲取該 bean 適用的增強(Advice)或攔截器(Advisor),在Seata中選擇圖上的實現:
??如果是普通全局事務,就使用GlobalTransactionalInterceptor
增強目標方法,執行其invoke
方法。
wrapIfNecessary方法在Spring啟動時,判斷是否需要為 Bean 創建事務代理,并注入相應的攔截器(TCC 或 AT 模式)。
1.2、handleGlobalTransaction
??在1.1中,創建了GlobalTransactionalInterceptor
的對象,其實現了MethodInterceptor
??在重寫了MethodInterceptor
的invoke
方法中,如果當前方法上存在@GlobalTransactional注解,則會調用到handleGlobalTransaction方法:
??在handleGlobalTransaction
方法中,首先會執行transactionalTemplate
的execute
方法:
??在執行execute
方法的過程中,可能會發生異常,handleGlobalTransaction
捕捉到異常后,會進行判斷,如果當前是事務的參與者,也就是RM,則繼續向上拋出異常,如果是事務的發起者TM,則解析出異常的code,執行各自失敗的邏輯(SeataAutoConfiguration
中注入的FailureHandler
的邏輯)。
??這里的TransactionalTemplate
,是seata的實現:
??在execute
方法中,首先會進行事務傳播級別的判斷,是否應該新開啟一個事務,掛起當前事務等操作:Seata 的傳播級別與 Spring 的傳播語義一致,但實現機制完全不同,Spring 操作本地事務,Seata 操作的是全局分布式事務上下文。
??然后就會執行一套標準的事務流程:開啟事務,執行事務,提交/回滾。
handleGlobalTransaction 方法代表了事務執行的整體流程。
1.3、invoke
??invoke
是在AOP攔截到對原始DataSource
的調用時,將調用路由到Seata的代理DataSource
上,使得數據庫連接支持全局事務的上下文控制。
- 判斷攔截的方法是否為
javax.sql.DataSource
接口中定義的方法。 - 獲取當前原始的
DataSource
,從DataSourceProxyHolde
中獲取其對應的SeataDataSourceProxy
,最終將方法調用代理到SeataDataSourceProxy
上。
Seata 使 SeataDataSourceProxy來增強連接的行為,如記錄 UndoLog,綁定 XID,向 TC 注冊分支事務
二、總結
Spring 容器啟動時
GlobalTransactionScanner
會掃描所有 bean 的方法是否包含@GlobalTransactional
和@GlobalLock
注解。- 如果有,就為該方法添加一個 AOP Advisor,代理增強邏輯就是
GlobalTransactionalInterceptor
。
方法執行時
- Spring AOP 會調用
GlobalTransactionalInterceptor#invoke()
。 - Seata 會根據事務傳播級別、當前事務上下文,決定是否發起或加入全局事務。
- 正常執行業務邏輯(調用
invocation.proceed()
)。 - 執行完畢后:
- 如果方法正常返回,Seata 提交全局事務;
- 如果方法拋出異常,Seata 回滾事務