天天看點

seata xid是什麼_seata源碼閱讀筆記

seata源碼閱讀筆記

本文沒有seata的使用方法,怎麼使用seata可以參考官方示例,詳細的很。

本文基于v0.8.0版本,本文沒貼代碼。

seata中的三個重要部分:

TC:事務協調器,維護全局事務和分支事務的狀态,驅動全局送出或復原,就是seata的服務端。

TM:事務管理器,開始全局事務,送出或復原全局事務。

RM:資料總管,管理正在處理的分支事務的資源,向TC注冊并報告分支事務的狀态,并驅動分支事務的送出或復原。

seata的初始化

TC啟動

RM發送jdbcUrl、applicationId和transactionServiceGroup三個參數向TC發起注冊,建立連接配接。入口是在DataSourceProxy執行個體化的時候

TM發送applicationId和transactionServiceGroup兩個參數向TC發起注冊,建立連接配接。

TM-處理全局事務

全局事務發起者,就是我們加@GlobalTransactional注解的方法,seata會代理我們的方法,通過以下步驟來完成全局事務。

發送全局事務開始請求到TC,TC傳回xid(全局事務唯一id),将xid綁定到目前線程

執行我們的業務邏輯

業務邏輯成功,發送全局事務commit請求到TC,如果失敗則重試

業務邏輯異常(可配置復原規則,即哪些異常復原),發送全局事務rollback請求到TC,如果失敗則重試

清理資源,事務結束

源碼閱讀入口:io.seata.spring.annotation.GlobalTransactionalInterceptor#invoke

RM-處理分支事務

rm需要代理我們項目中的資料源,這一步需要我們修改自己的代碼,如下:

@Configuration

public class DataSourceConfig {

@Bean

@ConfigurationProperties(prefix = "spring.datasource")

public DataSource druidDataSource() {

return new DruidDataSource();

}

@Primary

@Bean("dataSource")

public DataSource dataSource(DataSource druidDataSource) {

return new io.seata.rm.datasource.DataSourceProxy(druidDataSource);

}

代理資料源的主要目的是代理資料庫連接配接,這樣就可以控制分支事務的commit、rollback。

seate的rm-datasoure子產品中有這幾個代理類DataSourceProxy、ConnectionProxy、StatementProxy,通過代理jdbc中的這幾個類,讓我們的sql通過ExecuteTemplate來進行執行,這個類就是我們了解seata怎麼控制分支事務的入口。下面看分支事務的處理步驟:

将xid綁定到目前資料庫連接配接(xid怎麼來的?在後面)

拿到sql執行前資料庫表中的資料記錄beforeImage(隻是受影響的資料及字段,通過解析我們的sql完成)

執行我們的業務sql

拿到sql執行後資料庫表中的資料記錄afterImage

用變動資料的主鍵值作為lockKey,并儲存到目前資料庫連接配接中

将beforeImage和afterImage轉換成undo log,并儲存到目前資料庫連接配接中

向TC發送全局鎖請求(帶着xid和lockKey),TC傳回branchId,表示拿到了全局鎖,若不成功則重試(預設30次,每次間隔10ms);這個全局鎖的是為了防止多個使用者同時對同一資料進行修改,造成後面通過undo_log進行復原操作時資料出錯;這個鎖在PhaseTwo完成後,TC會進行釋放。

将undoLog儲存到資料庫的undo_log表中

送出資料庫事務

向TC報告目前分支事務commit的結果(失敗或成功),若報告不成功則重試(預設5次)

commit成功後,清理目前資料庫連接配接綁定的xid

commit失敗後,向上抛出異常,以便讓tm知道,進行全局事務的復原

這是PhaseOne的整個過程,下面看PhaseTwo:

tc收到tm的全局事務送出或復原請求後,會對這個全局事務中的所有分支事務進行通知,rm在收到通知後,進行復原或者送出的操作

復原:取出資料庫undo_log進行資料補償還原,成功後删除undo_log;送出:直接删除undo_log

傳回tc處理結果

源碼閱讀入口: io.seata.rm.datasource.StatementProxy#execute

XID在服務鍊路中的傳遞

dubbo

用dubbo的filter實作的,源碼:io.seata.integration.dubbo.TransactionPropagationFilter

原理就是:上遊在filter中把xid放到RpcContext中,下遊再從RpcContext拿到xid。

RestTemplate和Feign

對RestTemplate和Feign的支援不在seata-all中,而是在spring-cloud-alibaba-seata中

源碼入口:

com.alibaba.cloud.seata.rest.SeataRestTemplateAutoConfiguration

com.alibaba.cloud.seata.feign.SeataFeignClientAutoConfiguration

com.alibaba.cloud.seata.web.SeataHandlerInterceptorConfiguration

原理就是:上遊通過攔截器将xid放到請求的header中,下遊通過攔截器從header中拿到xid。

seata還支援很多RPC架構,如sofa-rpc、motan等。我們也可以通過類似的方法使seata支援我們自己的rpc架構。

GlobalLock注解

如果是用 GlobalLock 修飾的本地業務方法,雖然該方法并非某個全局事務下的分支事務,但是它對資料資源的操作也需要先查詢全局鎖,如果存在其他 Seata 全局事務正在修改,則該方法也需等待。是以,如果想要Seata 全局事務執行期間,資料庫不會被其他事務修改,則該方法需要強制添加 GlobalLock 注解,來将其納入 Seata 分布式事務的管理範圍。

功能有點類似于 Spring 的 @Transactional 注解,如果你希望開啟事務,那麼必須添加該注解,如果你沒有添加那麼事務功能自然不生效,業務可能出 BUG;Seata 也一樣,如果你希望某個不在全局事務下的 SQL 操作不影響 AT 分布式事務,那麼必須添加 GlobalLock 注解。

TC的高可用設計

seata服務端支援zk、nacos、eureka等作為服務發現,通過資料庫實作資料共享,全局事務Session資訊、分支事務Session資訊,全局鎖資訊都是放在資料庫中

TCC模式

TCC和AT的不同主要展現在RM這邊,TC和TM都是一樣的。

TCC的RM不會代理我們的資料源了,而是由我們自己指定rollback和commit邏輯,在二階段中,TM在發起全局事務送出復原時,RM隻需要執行我們指定的rollback和commit方法就行了。

這種模式就是我們業務代碼的變動要多些,效率是要比AT模式高些。

RM一階段源碼入口:io.seata.spring.tcc.TccActionInterceptor#invoke

RM二階段源碼入口:io.seata.rm.tcc.TCCResourceManager#branchCommit#branchRollback

參考: