Seata 四大模式詳解 分佈式事務實戰方案彙總 https://www.cnblogs.com/yizhiamumu/p/16625677.html 分佈式事務原理及解決方案案例

 

分佈式事務 參考文章:

分佈式事務實戰方案彙總 https://www.cnblogs.com/yizhiamumu/p/16625677.html

分佈式事務原理及解決方案案例 https://www.cnblogs.com/yizhiamumu/p/16662412.html

Spring Cloud集成Seata分佈式事務-TCC模式 https://www.cnblogs.com/yizhiamumu/p/16809367.html

Seata 四大模式詳解 https://www.cnblogs.com/yizhiamumu/p/16809386.html

Seata 核心源碼詳解 https://www.cnblogs.com/yizhiamumu/p/16811722.html

 

 

Seata分爲三大模塊,分別是 TM、RM 和 TC

TC (Transaction Coordinator) - 事務協調者:
維護全局和分支事務的狀態,驅動全局事務提交或回滾。

TM (Transaction Manager) - 事務管理器:
定義全局事務的範圍:開始全局事務、提交或回滾全局事務。

RM (Resource Manager) - 資源管理器:
管理分支事務處理的資源,與TC交談以註冊分支事務和報告分支事務的狀態,並驅動分支事務提交或回滾。

在 Seata 中,分佈式事務的執行流程:

TM 開啓分佈式事務(TM 向 TC 註冊全局事務記錄);
按業務場景,編排數據庫、服務等事務內資源(RM 向 TC 彙報資源準備狀態 );
TM 結束分佈式事務,事務一階段結束(TM 通知 TC 提交/回滾分佈式事務);
TC 彙總事務信息,決定分佈式事務是提交還是回滾;
TC 通知所有 RM 提交/回滾 資源,事務二階段結束。
TM 和 RM 是作爲 Seata 的客戶端與業務系統集成在一起,TC 作爲 Seata 的服務端獨立部署。

服務端存儲模式支持三種:

file: 單機模式,全局事務會話信息內存中讀寫並持久化本地文件root.data,性能較高(默認)

DB: 高可用模式,全局事務會話信息通過DB共享,相對性能差一些

redis: Seata-Server1.3及以上版本支持,性能較高,存在事務信息丟失風險,需要配合實際場景使用

TC環境搭建詳解
這裏我們使用DB高可用模式,找到conf/file.conf文件

修改以上中的信息,找到對應的db配置,修改其中的jdbc連接,要注意其中涉及到三個表(global_table,branch_table,lock_table),同時 mysql5和mysql8的驅動是不一樣的

mysql5:com.mysql.jdbc.Driver

mysql8:com.mysql.cj.jdbc.Driver

建表語句

-- -------------------------------- The script used when storeMode is 'db' --------------------------------
-- the table to store GlobalSession data
CREATE TABLE IF NOT EXISTS `global_table`
(
    `xid`                       VARCHAR(128) NOT NULL,
    `transaction_id`            BIGINT,
    `status`                    TINYINT      NOT NULL,
    `application_id`            VARCHAR(32),
    `transaction_service_group` VARCHAR(32),
    `transaction_name`          VARCHAR(128),
    `timeout`                   INT,
    `begin_time`                BIGINT,
    `application_data`          VARCHAR(2000),
    `gmt_create`                DATETIME,
    `gmt_modified`              DATETIME,
    PRIMARY KEY (`xid`),
    KEY `idx_status_gmt_modified` (`status` , `gmt_modified`),
    KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4;

-- the table to store BranchSession data
CREATE TABLE IF NOT EXISTS `branch_table`
(
    `branch_id`         BIGINT       NOT NULL,
    `xid`               VARCHAR(128) NOT NULL,
    `transaction_id`    BIGINT,
    `resource_group_id` VARCHAR(32),
    `resource_id`       VARCHAR(256),
    `branch_type`       VARCHAR(8),
    `status`            TINYINT,
    `client_id`         VARCHAR(64),
    `application_data`  VARCHAR(2000),
    `gmt_create`        DATETIME(6),
    `gmt_modified`      DATETIME(6),
    PRIMARY KEY (`branch_id`),
    KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4;

-- the table to store lock data
CREATE TABLE IF NOT EXISTS `lock_table`
(
    `row_key`        VARCHAR(128) NOT NULL,
    `xid`            VARCHAR(128),
    `transaction_id` BIGINT,
    `branch_id`      BIGINT       NOT NULL,
    `resource_id`    VARCHAR(256),
    `table_name`     VARCHAR(32),
    `pk`             VARCHAR(36),
    `status`         TINYINT      NOT NULL DEFAULT '0' COMMENT '0:locked ,1:rollbacking',
    `gmt_create`     DATETIME,
    `gmt_modified`   DATETIME,
    PRIMARY KEY (`row_key`),
    KEY `idx_status` (`status`),
    KEY `idx_branch_id` (`branch_id`),
    KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4;

CREATE TABLE IF NOT EXISTS `distributed_lock`
(
    `lock_key`       CHAR(20) NOT NULL,
    `lock_value`     VARCHAR(20) NOT NULL,
    `expire`         BIGINT,
    primary key (`lock_key`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4;

INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('AsyncCommitting', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryCommitting', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryRollbacking', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('TxTimeoutCheck', ' ', 0);

 

global_table: 全局事務表,每當有一個全局事務發起後,就會在該表中記錄全局事務的ID

branch_table: 分支事務表,記錄每一個分支事務的 ID,分支事務操作的哪個數據庫等信息

lock_table: 全局鎖

當上述配置好以後,重啓Seata即可生效。

Seata 配置 Nacos

Seata支持註冊服務到Nacos,以及支持Seata所有配置放到Nacos配置中心,在Nacos中統一維護;在
高可用模式下就需要配合Nacos來完成

首先找到 conf/registry.conf,修改registry信息

registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "nacos"

  nacos {
    application = "seata-server" # 這裏的配置要和客戶端保持一致
    serverAddr = "127.0.0.1:8848"
    group = "SEATA_GROUP"  # 這裏的配置要和客戶端保持一致
    namespace = ""
    cluster = "default"
    username = "nacos"
    password = "nacos"
  }
  config {
  # file、nacos 、apollo、zk、consul、etcd3
  type = "nacos"

  nacos {
    serverAddr = "127.0.0.1:8848"
    namespace = ""
    group = "SEATA_GROUP"
    username = "nacos"
    password = "nacos"
    dataId = "seataServer.properties"
  }
  ......
}


修改好後,將seata中的一些配置上傳到Nacos中,因爲配置項比較多,所以官方提供了一個config.txt,只下載並且修改其中某些參數後,上傳到Nacos中即可。

下載地址:https://github.com/seata/seata/tree/develop/script/config-center

修改項如下:

service.vgroupMapping.mygroup=default # 事務分組
store.mode=db
store.db.driverClassName=com.mysql.cj.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
store.db.user=root
store.db.password=123456

修改好這個文件以後,把這個文件放到seata目錄下

把這些配置都加入到Nacos配置中,要藉助一個腳本來進行執行,官方已經提供好。

地址爲:https://github.com/seata/seata/blob/develop/script/config-center/nacos/nacos-config.sh

新建一個nacos-config.sh文件,將腳本內容複製進去,修改congfig.txt的路徑

 

上述文件修改好後,打開git工具,將nacos-config.sh工具拖拽到窗體中即可或者使用命令

sh nacos-config.sh -h 127.0.0.1 -p 8848 -g SEATA_GROUP -t 88b8f583-43f9-4272-bd46-78a9f89c56e8 -u nacos -w nacos

-h:nacos地址

-p:端口,默認8848

-g:seata的服務列表分組名稱

-t:nacos命名空間id

-u和-w:nacos的用戶名和密碼

最後會有四個執行失敗,是因爲redis報錯的關係,這個可以忽略,不影響正常使用。最後可以看到在Nacos中有很多的配置項,說明導入成功。再重新其中seata,成功監聽到8091端口,表示前置工作都已經準備完成。

Seata的事務模式


Seata 定義了全局事務的框架,主要分爲以下幾步

TM 向 TC請求 發起(Begin)、提交(Commit)、回滾(Rollback)等全局事務
TM把代表全局事務的XID綁定到分支事務上
RM向TC註冊,把分支事務關聯到XID代表的全局事務中
RM把分支事務的執行結果上報給TC
TC發送分支提交(Branch Commit)或分支回滾(Branch Rollback)命令給RM

 

 

Seata 的 全局事務 處理過程,分爲兩個階段:

- 執行階段 :執行分支事務,並保證執行結果滿足是可回滾的(Rollbackable) 和持久化的(Durable)。
- 完成階段: 根據 執行階段 結果形成的決議,應用通過 TM 發出的全局提交或回滾的請求給 TC,TC 命令 RM 驅動 分支事務 進行 Commit 或 Rollback。

Seata 的所謂事務模式是指:運行在 Seata 全局事務框架下的 分支事務 的行爲模式。準確地講,應該叫作 分支事務模式。

不同的 事務模式 區別在於 分支事務 使用不同的方式達到全局事務兩個階段的目標。

即,回答以下兩個問題:

- 執行階段 :如何執行並 保證 執行結果滿足是可回滾的(Rollbackable) 和持久化的(Durable)。
- 完成階段: 收到 TC 的命令後,如何做到分支的提交或回滾?

我們以AT模式爲例:

- 執行階段:
可回滾:根據 SQL 解析結果,記錄回滾日誌
持久化:回滾日誌和業務 SQL 在同一個本地事務中提交到數據庫
- 完成階段:
分支提交:異步刪除回滾日誌記錄
分支回滾:依據回滾日誌進行反向補償更新

接下來就進入重頭戲,Seata四大模式的介紹。

Seata-XA模式

Seata 1.2.0 版本發佈了新的事務模型:XA模式,實現了對XA協議的支持。對於XA模式我們需要從三個點去解析它。

XA模式是什麼

爲什麼支持XA
XA模式如何實現和使用

XA模式簡介

首先需要知道XA模型是什麼,XA 規範早在上世紀 90 年代初就被提出,用於解決分佈式事務領域的問題,他也是最早的分佈式事務處理方案,因爲需要數據庫內部也是支持XA模式的,比如MYSQL,XA模式具有強一致性的特點,因此他對數據庫佔用時間比較長,所以性能比較低。

XA模式屬於兩階段提交。

第一階段進行事務註冊,將事務註冊到TC中,執行SQL語句。

第二階段TC判斷無事務出錯,通知所有事務提交,否則回滾。

在第一到第二階段過程中,事務一直佔有數據庫鎖,因此性能比較低,但是所有事務要麼一起提交,要麼一起回滾,所以能實現強一致性。

無論是AT模式、TCC還是SAGA,這些模式的提出,都是源於XA規範對某些業務場景無法滿足

 

什麼是XA協議


XA規範是X/OPEN組織定義的分佈式事務處理(DTP,Distributed Transaction Processing)標準,XA規範描述了全局事務管理器和局部資源管理器之間的接口,XA規範的目的是允許多個資源(如數據庫,應用服務器,消息隊列等)在同一事務中訪問,這樣可以使 ACID 屬性跨越應用程序而保持有效。

XA 規範 使用兩階段提交(2PC,Two-Phase Commit)來保證所有資源同時提交或回滾任何特定的事務。因爲XA規範最早被提出,所以幾乎所有的主流數據庫都保有對XA規範的支持。

分佈式事務DTP模型定義的角色如下:

AP:即應用程序,可以理解爲使用DTP分佈式事務的程序,例如訂單服務、庫存服務
RM:資源管理器,可以理解爲事務的參與者,一般情況下是指一個數據庫的實例(MySql),通過資源管理器對該數據庫進行控制,資源管理器控制着分支事務
TM:事務管理器,負責協調和管理事務,事務管理器控制着全局事務,管理實務生命週期,並協調各個RM。全局事務是指分佈式事務處理環境中,需要操作多個數據庫共同完成一個工作,這個工作即是一個全局事務。

 

DTP模式定義TM和RM之間通訊的接口規範叫XA,簡單理解爲數據庫提供的2PC接口協議,基於數據庫的XA協議來實現的2PC又稱爲XA方案。

現在有應用程序(AP)持有訂單庫和庫存庫,應用程序(AP)通過TM通知訂單庫(RM)和庫存庫(RM),進行扣減庫存和生成訂單,這個時候RM並沒有提交事務,而且鎖定資源。

當TM收到執行消息,如果有一方RM執行失敗,分別向其他RM也發送回滾事務,回滾完畢,釋放鎖資源

當TM收到執行消息,RM全部成功,向所有RM發起提交事務,提交完畢,釋放鎖資源。

 

 

分佈式通信協議XA規範,具體執行流程如下所示:

第一步:AP創建了RM1,RM2的JDBC連接。

第二步:AP通知生成全局事物ID,並把RM1,RM2註冊到全局事務ID

第三步:執行二階段協議中的第一階段prepare

第四步:根據prepare請求,決定整體提交或回滾。

但是對於XA而言,如果一個參與全局事務的資源“失聯”了,那麼就意味着TM收不到分支事務結束的命令,那麼它鎖定的數據,將會一直被鎖定,從而產生死鎖,這個也是Seata需要重點解決的問題。

在Seata定義的分佈式事務架構中,利用事務資源(數據局、消息)等對XA協議進行支持,用XA協議的機制來管理分支事務。

 

 

執行階段:

可回滾:業務SQL操作在XA分支中進行,有資源管理器對XA協議的支持來保證可回滾
持久化:ZA分支完成以後,執行 XA prepare,同樣,由資源對XA協議的支持來保證持久化
完成階段:

分支提交:執行XA分支的commit
分支回滾:執行XA分支的rollback

XA存在的意義

Seata 已經支持了三大事務模式:AT\TCC\SAGA,這三個都是補償型事務,補償型事務處理你機制構建在 事務資源 之上(要麼中間件層面,要麼應用層),事務資源本身對於分佈式的事務是無感知的,這種對於分佈式事務的無感知存在有一個根本性的問題,無法做到真正的全局一致性。

例如一個庫存記錄,在補償型事務處理過程中,用80扣減爲60,這個時候倉庫管理員查詢數據結果,看到的是60,之後因爲異常回滾,庫存回滾到原來的80,那麼這個時候庫存管理員看到的60,其實就是髒數據,而這個中間狀態就是補償型事務存在的髒數據。

和補償型事務不同,XA協議要求事務資源 本身提供對規範和協議的支持,因爲事務資源感知並參與分佈式事務處理過程中,所以事務資源可以保證從任意視角對數據的訪問有效隔離性,滿足全局數據的一致性。

XA模式的使用
官方案例:https://github.com/seata/seata-samples

項目名:seata-samples

業務開始: business-xa
庫存服務: stock-xa
訂單服務: order-xa
賬號服務: account-xa

把這個項目案例下載下來以後,找到項目名爲seata-xa的目錄,裏面有測試數據庫的鏈接,如果不想用測試數據庫,只需要修改官方文檔中數據庫配置信息即可。

首先關注的是 business-xa項目,更多的關注BusinessService.purchase()方法

      @GlobalTransactional
    public void purchase(String userId, String commodityCode, int orderCount, boolean rollback) {
        String xid = RootContext.getXID();
        LOGGER.info("New Transaction Begins: " + xid);
        
        //調用庫存減庫存
        String result = stockFeignClient.deduct(commodityCode, orderCount);

        if (!SUCCESS.equals(result)) {
            throw new RuntimeException("庫存服務調用失敗,事務回滾!");
        }

        //生成訂單
        result = orderFeignClient.create(userId, commodityCode, orderCount);

        if (!SUCCESS.equals(result)) {
            throw new RuntimeException("訂單服務調用失敗,事務回滾!");
        }

        if (rollback) {
            throw new RuntimeException("Force rollback ... ");
        }
    }

其實現方法較之前差不多,我們只需要在order-xa裏面(OrderService.create),添加人爲錯誤(int i = 1/0;)

    public void create(String userId, String commodityCode, Integer count) {
        String xid = RootContext.getXID();
        LOGGER.info("create order in transaction: " + xid);

        int i = 1/0;

        // 定單總價 = 訂購數量(count) * 商品單價(100)
        int orderMoney = count * 100;
        // 生成訂單
        jdbcTemplate.update("insert order_tbl(user_id,commodity_code,count,money) values(?,?,?,?)",
            new Object[] {userId, commodityCode, count, orderMoney});
        // 調用賬戶餘額扣減
        String result = accountFeignClient.reduce(userId, orderMoney);
        if (!SUCCESS.equals(result)) {
            throw new RuntimeException("Failed to call Account Service. ");
        }

    }

裏面有一個方法可以進行XA模式和AT模式的轉換OrderXADataSourceConfiguration.dataSource

    @Bean("dataSourceProxy")
    public DataSource dataSource(DruidDataSource druidDataSource) {
        // DataSourceProxy for AT mode
        // return new DataSourceProxy(druidDataSource);

        // DataSourceProxyXA for XA mode
        return new DataSourceProxyXA(druidDataSource);
    }

我們啓動這四個服務,訪問地址 http://localhost:8084/purchase

我們可以其中報錯,然後再去看對應數據庫的數據,沒有發生更改,說明我們的XA模式生效了,當你dubug去看裏面的庫存服務的時候,當操作數據更改的時候,數據庫裏面其實也是沒有記錄的,因爲XA是強一致性,只有當事務結束完成以後,纔會更改其中的數據。

XA模式的加入,補齊了Seata在全局一致性場景下的缺口,形成了AT、TCC、Saga、XA 四大事務模式的版圖,基本滿足了所有場景分佈式事務處理的需求。

其中XA和AT是無業務侵入的,而TCC和Saga是有一定業務侵入的。

Seata-AT模式

先來介紹一下AT模式,AT模式是一種沒有侵入的分佈式事務的解決方案,在AT模式下,用戶只需關注自己的業務SQL,用戶的業務SQL作爲一階段,Seata框架會自動生成事務進行二階段提交和回滾操作。

兩階段提交協議的演變:

  • 一階段:業務數據和回滾日誌記錄在同一個本地事務中提交,釋放本地鎖和連接資源。

  • 二階段:

    提交異步化,非常快速地完成。

    回滾通過一階段的回滾日誌進行反向補償。

 

AT模式主要特點

1 最終一致性
2 性能較XA高
3 只在第一階段獲取鎖,在第一階段進行提交後釋放鎖。

在一階段中,Seata會攔截 業務SQL ,首先解析SQL語義,找到要操作的業務數據,在數據被操作前,保存下來記錄 undo log,然後執行 業務SQL 更新數據,更新之後再次保存數據 redo log,最後生成行鎖,這些操作都在本地數據庫事務內完成,這樣保證了一階段的原子性。

相對一階段,二階段比較簡單,負責整體的回滾和提交,如果之前的一階段中有本地事務沒有通過,那麼就執行全局回滾,否在執行全局提交,回滾用到的就是一階段記錄的 undo Log ,通過回滾記錄生成反向更新SQL並執行,以完成分支的回滾。當然事務完成後會釋放所有資源和刪除所有日誌。

AT流程分爲兩階段,主要邏輯全部在第一階段,第二階段主要做回滾或日誌清理的工作。流程如下:

 

從上圖中我們可以看到,訂單服務中TM向TC申請開啓一個全局事務,一般通過@GlobalTransactional標註開啓,TC會返回一個全局事務ID(XID),訂單服務在執行本地事務之前,RM會先向TC註冊一個分支事務,
訂單服務依次生成undo log 執行本地事務,生成redo log 提交本地事務,向TC彙報,事務執行OK。

訂單服務發起遠程調用,將事務ID傳遞給庫存服務,庫存服務在執行本地事務之前,先向TC註冊分支事務,庫存服務同樣生成undo Log和redo Log,向TC彙報,事務狀態成功。

如果正常全局提交,TC通知RM一步清理掉本地undo和redo日誌,如果存在一個服務執行失敗,那麼發起回滾請求。通過undo log進行回滾。

在這裏還會存在一個問題,因爲每個事務從本地提交到通知回滾這段時間裏面,可能這條數據已經被其他事務進行修改,如果直接用undo log進行回滾,可能會導致數據不一致的情況,

這個時候 RM會用 redo log進行驗證,對比數據是否一樣,從而得知數據是否有別的事務進行修改過,undo log是用於被修改前的數據,可以用來回滾,redolog是用於被修改後的數據,用於回滾校驗。

如果數據沒有被其他事務修改過,可以直接進行回滾,如果是髒數據,redolog校驗後進行處理。

實戰

瞭解了AT模型的基本操作,接下來就來實戰操作一下,關於AT模型具體是如何實現的。首先設計兩個服務 cloud-alibaba-seata-order 和 cloud-alibaba-seata-stock

表結構t_order、t_stock和undo_log三張表,項目源碼和表結構,加上undo_log表,此表用於數據的回滾,文末有鏈接。

cloud-alibaba-seata-order核心代碼如下:

controller

@RestController
public class OrderController {

    @Autowired
    private OrderService orderService;

    @GetMapping("order/create")
    @GlobalTransactional //開啓分佈式事務
    public String create(){
        orderService.create();
        return "訂單創建成功!";
    }

}

OrderService

public interface OrderService {
    void create();
}

StockClient

@FeignClient(value = "seata-stock")
public interface StockClient {
    @GetMapping("/stock/reduce")
    String reduce();

}

OrderServiceImpl

@Service
public class OrderServiceImpl implements OrderService{

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private StockClient stockClient;

    @Override
    public void create() {
        //扣減庫存
        stockClient.reduce();
        System.out.println("扣減庫存成功!");

        //手工異常 用於回滾庫存信息
        int i = 1/0;
        System.err.println("異常!");

        //創建訂單
        orderMapper.createOrder();
        System.out.println("創建訂單成功!");
    }
}

OrderMapper

@Mapper
public interface OrderMapper {

    @Insert("insert into t_order (order_no,order_num) value (order_no+1,1)")
    void createOrder();

}

cloud-alibaba-seata-stock核心代碼如下:

@RestController
public class StockController {

    @Autowired
    private StockService stockService;

    @GetMapping("stock/reduce")
    public String reduce(){
        stockService.reduce();
        return "庫存數量已扣減:"+ new Date();
    }
}

 

public interface StockService {
    void reduce();
}

 

@Service
public class StockServiceImpl implements StockService{

    @Autowired
    StockMapper stockMapper;

    @Override
    public void reduce() {
        stockMapper.reduce();
    }
}

 

@Mapper
@Repository
public interface StockMapper {

    @Update("update t_stock set order_num = order_num - 1 where order_no = 1 ")
    void reduce();

}

代碼都比較簡單,首先我們需要將order和stock服務都跑起來,在之前我們的Nacos和Seata都要啓動起來,這個時候我們訪問order的Rest接口,http://localhost:8087/order/create,

爲了驗證undo_log的表是用於存儲回滾數據,我們在OrderServiceImpl.create()中添加斷點,用debug的方式啓動

然後訪問http://localhost:8087/order/create,當程序卡在這個節點的時間,我們去看undo_log和庫存表,會發現,庫存確實減少了,而且undo_log也出現了對應的快照記錄修改當前的數據信息,這個數據就是用來回滾的數據,

但是當我們F9通過以後,庫存數量恢復,並且undo_log表的數據行也沒有了,這個時候證明我們的Seata事務生效,回滾成功。

到這裏我們就驗證了AT事務的執行過程,相比於XA和TCC等事務模型,Seata的AT模型可以應對大多數的業務場景,並且可以做到無業務侵入,開發者無感知,對於整個事務的協調、提交或者回滾操作,都可以通過AOP完成,開發者只需要關注業務即可。

由於Seata需要在不同的服務之間傳遞全局唯一的事務ID,和Dubbo等框架集成會比較友好,例如Dubbo可以用過隱士傳參來進行事務ID的傳遞,整個事務ID的傳播過程對開發者也可以做到無感知。

Seata-TCC模式

什麼是TCC
TCC 是分佈式事務中的二階段提交協議,它的全稱爲 Try-Confirm-Cancel,即資源預留(Try)、確認操作(Confirm)、取消操作(Cancel),他們的具體含義如下:

Try:對業務資源的檢查並預留;
Confirm:對業務處理進行提交,即 commit 操作,只要 Try 成功,那麼該步驟一定成功;
Cancel:對業務處理進行取消,即回滾操作,該步驟回對 Try 預留的資源進行釋放。

TCC 是一種侵入式的分佈式事務解決方案,以上三個操作都需要業務系統自行實現,對業務系統有着非常大的入侵性,設計相對複雜,

但優點是 TCC 完全不依賴數據庫,能夠實現跨數據庫、跨應用資源管理,對這些不同數據訪問通過侵入式的編碼方式實現一個原子操作,更好地解決了在各種複雜業務場景下的分佈式事務問題。

TCC和AT區別

AT 模式基於 支持本地 ACID 事務 的 關係型數據庫:

一階段 prepare 行爲:在本地事務中,一併提交業務數據更新和相應回滾日誌記錄。
二階段 commit 行爲:馬上成功結束,自動 異步批量清理回滾日誌。
二階段 rollback 行爲:通過回滾日誌,自動 生成補償操作,完成數據回滾。
相應的,TCC 模式,不依賴於底層數據資源的事務支持:

一階段 prepare 行爲:調用自定義 的 prepare 邏輯。
二階段 commit 行爲:調用自定義 的 commit 邏輯。
二階段 rollback 行爲:調用自定義 的 rollback 邏輯。
所謂 TCC 模式,是指支持把 自定義 的分支事務納入到全局事務的管理中。

特點:

侵入性比較強,並且需要自己實現相關事務控制邏輯
在整個過程基本沒有鎖,性能較強

Seata-Saga模式

Saga模式是SEATA提供的長事務解決方案,在Saga模式中,業務流程中每個參與者都提交本地事務,當出現某一個參與者失敗則補償前面已經成功的參與者,一階段正向服務和二階段補償服務(執行處理時候出錯了,給一個修復的機會)都由業務開發實現。

Saga 模式下分佈式事務通常是由事件驅動的,各個參與者之間是異步執行的,Saga 模式是一種長事務解決方案。

之前我們學習的Seata分佈式三種操作模型中所使用的的微服務全部可以根據開發者的需求進行修改,但是在一些特殊環境下,比如老系統,封閉的系統(無法修改,同時沒有任何分佈式事務引入),那麼AT、XA、TCC模型將全部不能使用,爲了解決這樣的問題,才引用了Saga模型。

比如:事務參與者可能是其他公司的服務或者是遺留系統,無法改造,可以使用Saga模式。

Saga模式是Seata提供的長事務解決方案,提供了異構系統的事務統一處理模型。在Saga模式中,所有的子業務都不在直接參與整體事務的處理(只負責本地事務的處理),而是全部交由了最終調用端來負責實現,而在進行總業務邏輯處理時,在某一個子業務出現問題時,則自動補償全面已經成功的其他參與者,這樣一階段的正向服務調用和二階段的服務補償處理全部由總業務開發實現。

Saga狀態機

目前Seata提供的Saga模式只能通過狀態機引擎來實現,需要開發者手工的進行Saga業務流程繪製,並且將其轉換爲Json配置文件,而後在程序運行時,將依據子配置文件實現業務處理以及服務補償處理,而要想進行Saga狀態圖的繪製,一般需要通過Saga狀態機來實現。

基本原理:

通過狀態圖來定義服務調用的流程並生成json定義文件
狀態圖中一個節點可以調用一個服務,節點可以配置它的補償節點
狀態圖 json 由狀態機引擎驅動執行,當出現異常時狀態引擎反向執行已成功節點對應的補償節點將事務回滾
可以實現服務編排需求,支持單項選擇、併發、子流程、參數轉換、參數映射、服務執行狀態判斷、異常捕獲等功能

Saga狀態機的應用

官方文檔地址:https://seata.io/zh-cn/docs/user/saga.html

Seata Saga 狀態機可視化圖形設計器, 基於 GGEditor

運行

$ git clone https://github.com/seata/seata.git
$ cd saga/seata-saga-statemachine-designer
$ npm install
$ npm start

打包

$ cd saga/saga-statemachine-designer
$ npm run build

然後將index.html和dist目錄拷貝到web server的靜態頁面目錄下

使用

瞭解狀態機的種狀態類型,請看Saga的文檔。 通過設計器完成設計後可以點擊工具欄的'Json View'按鈕切換到Json視圖,將Json拷貝保存到自己應用的工程裏。雖然設計器生成的Json與Saga標準的Json有所差別(因爲設計器生成的json帶有佈局信息),但狀態機可以直接加載,它會將其轉化成Saga狀態機標準的Json。

 

總結

總的來說,AT模式實現的是最終一致性,所以可能存在中間狀態,而XA模式實現的強一致性,所以效率較低一點,

而Saga可以用來處理不同開發語言之間的分佈式事務,所以關於分佈式事務的四大模型,基本可以滿足所有的業務場景,其中XA和AT沒有業務侵入性,而Saga和TCC具有一定的業務侵入。

在Seata的中AT模式基本可以滿足百分之80的分佈式事務的業務需求

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章