seata源碼閱讀筆記

seata源碼閱讀筆記

  • 本文沒有seata的使用方法,怎麼使用seata能夠參考官方示例,詳細的很。
  • 本文基於v0.8.0版本,本文沒貼代碼。
  • seata中的三個重要部分:
    1. TC:事務協調器,維護全局事務和分支事務的狀態,驅動全局提交或回滾,就是seata的服務端。
    2. TM:事務管理器,開始全局事務,提交或回滾全局事務。
    3. RM:資源管理器,管理正在處理的分支事務的資源,向TC註冊並報告分支事務的狀態,並驅動分支事務的提交或回滾。

seata的初始化

  1. TC啓動
  2. RM發送jdbcUrl、applicationId和transactionServiceGroup三個參數向TC發起註冊,創建鏈接。入口是在DataSourceProxy實例化的時候
  3. TM發送applicationId和transactionServiceGroup兩個參數向TC發起註冊,創建鏈接。

TM-處理全局事務

全局事務發起者,就是咱們加@GlobalTransactional註解的方法,seata會代理咱們的方法,經過如下步驟來完成全局事務。java

  1. 發送全局事務開始請求到TC,TC返回xid(全局事務惟一id),將xid綁定到當前線程
  2. 執行咱們的業務邏輯
  3. 業務邏輯成功,發送全局事務commit請求到TC,若是失敗則重試
  4. 業務邏輯異常(可配置回滾規則,即哪些異常回滾),發送全局事務rollback請求到TC,若是失敗則重試
  5. 清理資源,事務結束

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

RM-處理分支事務

rm須要代理咱們項目中的數據源,這一步須要咱們修改本身的代碼,以下:github

@Configuration
public class DataSourceConfig {

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource() {
        return new DruidDataSource();
    }

    /**
     * 須要將DataSourceProxy 設置爲主數據源,不然事務沒法回滾
     */
    @Primary
    @Bean("dataSource")
    public DataSource dataSource(DataSource druidDataSource) {
        return new io.seata.rm.datasource.DataSourceProxy(druidDataSource);
    }

代理數據源的主要目的是代理數據庫鏈接,這樣就能夠控制分支事務的commit、rollback。web

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

  1. 將xid綁定到當前數據庫鏈接(xid怎麼來的?在後面)
  2. 拿到sql執行前數據庫表中的數據記錄beforeImage(只是受影響的數據及字段,經過解析咱們的sql完成)
  3. 執行咱們的業務sql
  4. 拿到sql執行後數據庫表中的數據記錄afterImage
  5. 用變更數據的主鍵值做爲lockKey,並保存到當前數據庫鏈接中
  6. 將beforeImage和afterImage轉換成undo log,並保存到當前數據庫鏈接中
  7. 向TC發送全局鎖請求(帶着xid和lockKey),TC返回branchId,表示拿到了全局鎖,若不成功則重試(默認30次,每次間隔10ms);這個全局鎖的是爲了防止多個用戶同時對同一數據進行修改,形成後面經過undo_log進行回滾操做時數據出錯;這個鎖在PhaseTwo完成後,TC會進行釋放。
  8. 將undoLog保存到數據庫的undo_log表中
  9. 提交數據庫事務
  10. 向TC報告當前分支事務commit的結果(失敗或成功),若報告不成功則重試(默認5次)
  11. commit成功後,清理當前數據庫鏈接綁定的xid
  12. commit失敗後,向上拋出異常,以便讓tm知道,進行全局事務的回滾

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

  1. tc收到tm的全局事務提交或回滾請求後,會對這個全局事務中的全部分支事務進行通知,rm在收到通知後,進行回滾或者提交的操做
  2. 回滾:取出數據庫undo_log進行數據補償還原,成功後刪除undo_log;提交:直接刪除undo_log
  3. 返回tc處理結果

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

XID在服務鏈路中的傳遞

dubbo

用dubbo的filter實現的,源碼:io.seata.integration.dubbo.TransactionPropagationFilterapp

原理就是:上游在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

參考:

Seata AT 模式分佈式事務源碼分析

相關文章
相關標籤/搜索