快速瞭解阿里微服務熱門開源分散式事務框架——Seata

風平浪靜如碼發表於2020-11-12

一、Seata 概述

Seata 是 Simple Extensible Autonomous Transaction Architecture 的簡寫,由 feascar 改名而來。

Seata 是阿里開源的分散式事務框架,屬於二階段提交模式。

目前github上已經有 12267 顆星了,也很活躍,最新的提交時間很多都是幾天前。

首先我們回顧一下在單體應用中,例如一個業務呼叫了3個模組,他們都使用同一個資料來源,是靠本地事務來保證事務一致性。

但在微服務架構中,這3個模組會變為3個獨立的微服務,各自有自己的資料來源,呼叫邏輯就變為:

Seata 如何處理呢?

Business 是業務入口,在程式中會通過註解來說明他是一個全域性事務,這時他的角色為 TM(事務管理者)。

Business 會請求 TC(事務協調器,一個獨立執行的服務),說明自己要開啟一個全域性事務,TC 會生成一個全域性事務ID(XID),並返回給 Business。

Business 得到 XID 後,開始呼叫微服務,例如呼叫 Storage。

和上面的圖一樣,方便檢視,防止滾到到這兒時已經看不到上面的圖片了

Storage 會收到 XID,知道自己的事務屬於這個全域性事務。Storage 執行自己的業務邏輯,操作本地資料庫。

Storage 會把自己的事務註冊到 TC,作為這個 XID 下面的一個分支事務,並且把自己的事務執行結果也告訴 TC。

此時 Storage 的角色是 RM(資源管理者),資源是指本地資料庫。

Order、Account 的執行邏輯與 Storage 一致。

在各個微服務都執行完成後,TC 可以知道 XID 下各個分支事務的執行結果,TM(Business) 也就知道了。

Business 如果發現各個微服務的本地事務都執行成功了,就請求 TC 對這個 XID 提交,否則回滾。

TC 收到請求後,向 XID 下的所有分支事務發起相應請求。

各個微服務收到 TC 的請求後,執行相應指令,並把執行結果上報 TC。

重要機制

(1)全域性事務的回滾是如何實現的呢?

Seata 有一個重要的機制:回滾日誌

每個分支事務對應的資料庫中都需要有一個回滾日誌表 UNDO_LOG,在真正修改資料庫記錄之前,都會先記錄修改前的記錄值,以便之後回滾。

在收到回滾請求後,就會根據 UNDO_LOG 生成回滾操作的 SQL 語句來執行。

如果收到的是提交請求,就把 UNDO_LOG 中的相應記錄刪除掉。

(2)RM 是怎麼自動和 TC 互動的?

是通過監控攔截JDBC實現的,例如監控到開啟本地事務了,就會自動向 TC 註冊、生成回滾日誌、向 TC 彙報執行結果。

(3)二階段回滾失敗怎麼辦?

例如 TC 命令各個 RM 回滾的時候,有一個微服務掛掉了,那麼所有正常的微服務也都不會執行回滾,當這個微服務重新正常執行後,TC 會重新執行全域性回滾。

1.3 核心元件

回顧一下其中的核心元件

  • 事務協調器 TC

維護全域性和分支事務的狀態,指示全域性提交或者回滾。

  • 事務管理者 TM

開啟、提交或者回滾一個全域性事務。

  • 資源管理者 RM

管理執行分支事務的那些資源,向TC註冊分支事務、上報分支事務狀態、控制分支事務的提交或者回滾。

1.4 具體工作過程

再從巨集觀上梳理一下 Seata 的工作過程:

  • TM 請求 TC,開始一個新的全域性事務,TC 會為這個全域性事務生成一個 XID。
  • XID 通過微服務的呼叫鏈傳遞到其他微服務。
  • RM 把本地事務作為這個XID的分支事務註冊到TC。
  • TM 請求 TC 對這個 XID 進行提交或回滾。
  • TC 指揮這個 XID 下面的所有分支事務進行提交、回滾。

二、Seata 詳細工作流程示例

下面我們通過一個分支事務的執行過程來了解 Seata 的工作流程。

例如有一個業務表 product(id,name),分支事務的業務邏輯:

update product set name = 'GTS' where name = 'TXC';

2.1 一階段

(1)解析 SQL

得到 SQL 的型別(UPDATE),表(product),條件(where name = 'TXC')等相關的資訊。

(2)查詢前映象

根據解析得到的條件資訊,生成查詢語句,定位資料。

select id, name from product where name = 'TXC';

得到前映象:

id name
1 TXC

(3)執行業務 SQL

執行自己的業務邏輯:

update product set name = 'GTS' where name = 'TXC';

把 name 改為了 GTS

(4)查詢後映象

根據前映象的結果,通過 主鍵 定位資料。

select id, name from product where id = 1;

得到後映象:

id name
1 GTS

(5)插入回滾日誌

把前後映象資料以及業務 SQL 相關的資訊組成一條回滾日誌記錄,插入到 UNDO_LOG 表中。

(6)提交前,向 TC 註冊分支:申請 product 表中,主鍵值等於 1 的記錄的 全域性鎖 。

(7)本地事務提交:業務資料的更新和前面步驟中生成的 UNDO LOG 一併提交。

(8)將本地事務提交的結果上報給 TC。

2.2 二階段 - 回滾

(1)收到 TC 的分支回滾請求,開啟一個本地事務,執行如下操作。

(2)通過 XID 和 Branch ID 查詢到相應的 UNDO LOG 記錄。

(3)資料校驗

拿 UNDO LOG 中的後鏡與當前資料進行比較,根據校驗結果決定是否做回滾。

(4)根據 UNDO LOG 中的前映象和業務 SQL 的相關資訊生成並執行回滾的語句:

update product set name = 'TXC' where id = 1;

(5)提交本地事務

並把本地事務的執行結果(即分支事務回滾的結果)上報給 TC。

二階段 - 提交

(1)收到 TC 的分支提交請求,把請求放入一個非同步任務的佇列中,馬上返回提交成功的結果給 TC。

(2)非同步任務階段的分支提交請求,將非同步和批量地刪除相應 UNDO LOG 記錄。

三、小結

上面介紹的是 Seata 的 AT 模式,就是自動化事務,使用非常簡單,對業務程式碼沒有侵入性。

不足的地方是目前文件比較少,網上的相關材料也不是很多,所以使用過程中遇到問題時可能就需要自己檢視原始碼,分析原理。

Seata 還支援 TCC 和 Saga 模式,但支援的主要方式是 AT。

寫在最後

歡迎大家關注我的公眾號【風平浪靜如碼】,海量Java相關文章,學習資料都會在裡面更新,整理的資料也會放在裡面。

覺得寫的還不錯的就點個贊,加個關注唄!點關注,不迷路,持續更新!!!

相關文章