分散式事務(七)之Seata簡介

御狐神發表於2021-11-23

在前面的文章中,我們介紹了分散式事務的概念以及一些解決方案。fenSeata是一款開源的分散式事務解決方案,致力於提供高效能和簡單易用的分散式事務服務。Seata將為使用者提供了AT、TCC、SAGA和XA事務模式,為使用者打造一站式的分散式解決方案。

Seata介紹

本文以一個使用者下單購買商品的系統為例,介紹開源框架Seata的原理和使用,下單該系統涉及三部分服務:

  1. 倉儲服務:對給定的商品扣除倉儲數量;
  2. 訂單服務:根據採購需求建立訂單;
  3. 帳戶服務:從使用者帳戶中扣除餘額;

分散式事務的主要作用是保證微服務情況下使用者下單過程中資料的一致性。這裡的一致性可以這樣理解:不會出現使用者餘額扣除成功,但是倉儲和訂單相關操作失敗的場景,三者要麼同時成功,要麼同時失敗。

單機事務場景

如果使用者下單購買商品涉及到的服務都在一個傳統的單機服務中,三部分服務可以共享同一個資料庫例項。這種情況下,我們可以通過本地事務的一致性保證倉儲/訂單/賬戶三者之間資料的一致性。

本地事務

如上圖所示,在單機服務中,三部分內容共用同一個資料庫例項,所以我們只需要本地事務就可以解決資料的一致性,以Spring框架為例,我們只需要在方法上新增@Transaction註解就可以實現整個購買流程中資料的一致性:

@Transaction
public void purchase(){
    doStoreBusiness();
    doOrderBusiness();
    doAccountBusiness();
}

分散式事務場景

在微服務框架中,倉儲/訂單/賬戶服務部署在不同的伺服器上,使用不同的資料庫例項,與單機模式完全不同。單機模式中的事務通常要求事務涉及的資料來源為同一個,並且事務涉及的資料庫操作在同一個資料庫連結中,分散式情況下顯然不滿足條件。

所以在分散式場景如何保證資料的一致性呢?這就是Seata需要解決的問題了。

分散式場景

Seata解決方案

Seata是用於解決分散式事務的開源框架,其內部有關於分散式事務的定義如下:分散式事務是由多個分支事務組成的全域性事務,其中每個分支事務都是本地事務的形式。

全域性事務

Seata框架包含三部分內容:

  1. 事務協調器(Transaction Coordinator,TC):維護全域性事務和分支事務的狀態,進行全域性事務提交或全域性事務回滾;
  2. 事務管理器(Transaction Manager,TM):定義全域性事務,開啟全域性事務、提交全域性事務或回滾全域性事務;
  3. 資源管理器(Resource Manager,RM):管理分支事務中的資源,向事務管理器註冊分支事務和並報告分支事務的狀態,負責分支事務提交或回滾;

Seata框架

一個典型的seata分散式事務的流程如下:

  1. TM向TC發出開啟全域性事務請求,TC生成全域性事務的唯一標識XID,設此處的全域性事務為T1;
  2. 在全域性事務T1的各個流程中,XID會作為事務的標識在微服務之間流轉;
  3. RM向TC註冊本地事務,註冊的本地事務的會作為全域性事務T1的分支事務;
  4. TM可以請求TC控制全域性事務T1提交或全域性事務T1回滾;
  5. TC可以請求全域性事務T1下的所有分支事務提交或回滾;

Seata流程

Seata歷史

阿里巴巴:

  • TXC:淘寶交易系統的分散式事務框架,阿里巴巴中介軟體團隊自2014年開始啟動該專案,用於解決應用架構從單一服務向微服務轉變所帶來的分散式事務問題;
  • GTS:全球交易服務。TXC作為阿里雲中介軟體產品,新名稱GTS於2016年釋出;
  • Fescar:2019年開始了基於TXC/GTS的開源專案Fescar,用於開源專案社群發展;

螞蟻金服:

  • XTS:擴充套件事務服務。螞蟻金服中介軟體團隊自2007年開始開發分散式事務中介軟體,該中介軟體在螞蟻金服中得到廣泛應用,解決了跨資料庫和服務的資料一致性問題。
  • DTX:分散式事務擴充套件。自2013年以來,XTS已在螞蟻金融雲上釋出,名稱為DTX;

Seata社群:

  • Seata:簡單的可擴充套件分散式事務解決方案,螞蟻金服將Fedscar改名為Seata並開源,使其成為一箇中立、開放的分散式事務社群。

Seata Maven依賴

<seata.version>1.4.2</seata.version>

<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-all</artifactId>
    <version>${seata.version}</version>
</dependency>

Seata案例

以上文中的使用者下單購買商品的系統為例,展示Seata的使用方式:

  1. 倉儲微服務:對給定的商品扣除倉儲數量;
  2. 訂單微服務:根據採購需求建立訂單;
  3. 帳戶微服務:從使用者帳戶中扣除餘額;

分散式場景

服務介面的定義

對於三個微服務,我們用三個介面來抽象其內部的邏輯:

  • 倉儲服務

    public interface StorageService {
    
          /**
          * 扣除儲存數量
          */
          void deduct(String commodityCode, int count);
      }
    
  • 訂單服務

      public interface OrderService {
          /**
          * 建立訂單
          */
          Order create(String userId, String commodityCode, int orderCount);
      }
    
  • 帳戶服務

    public interface AccountService {
    
        /**
        * 從使用者賬戶中借出
        */
        void debit(String userId, int money);
    }
    

主要業務邏輯

對於使用者下單購買商品的邏輯,我們用以下程式碼來實現:

  • 主要業務邏輯

      public class BusinessServiceImpl implements BusinessService {
    
          private StorageService storageService;
    
          private OrderService orderService;
    
          /**
          * 採購
          */
          public void purchase(String userId, String commodityCode, int orderCount) {
    
              storageService.deduct(commodityCode, orderCount);
    
              orderService.create(userId, commodityCode, orderCount);
          }
      }
    
  • 訂單服務業務邏輯

      public class OrderServiceImpl implements OrderService {
    
          private OrderDAO orderDAO;
    
          private AccountService accountService;
    
          public Order create(String userId, String commodityCode, int orderCount) {
    
              int orderMoney = calculate(commodityCode, orderCount);
    
              accountService.debit(userId, orderMoney);
    
              Order order = new Order();
              order.userId = userId;
              order.commodityCode = commodityCode;
              order.count = orderCount;
              order.money = orderMoney;
    
              // INSERT INTO orders ...
              return orderDAO.insert(order);
          }
      }
    

引入Seata

在服務中引入Seata服務之後,我們只需要在分散式事務最外層的方法上新增分散式事務註解@GlobalTransactional

@GlobalTransactional
public void purchase(String userId, String commodityCode, int orderCount) {
    // ......
}

新增註解之後,在執行業務邏輯之前,Seata會先生成全域性事務ID,並且在呼叫其它服務時,會在請求中攜帶全域性事務ID。如果其它微服務也新增了Seata依賴,這些微服務會獲取全域性事務ID,並且參與到全域性事務中。

本文只是簡單介紹以下Seata框架,具體工作原理在後續文章中詳細介紹。

Seata流程

我是御狐神,歡迎大家關注我的微信公眾號:wzm2zsd

qrcode_for_gh_83670e17bbd7_344-2021-09-04-10-55-16

參考文件

Seata官方文件

本文最先發布至微信公眾號,版權所有,禁止轉載!

相關文章