分佈式事務(七)之Seata簡介
在前面的文章中,我們介紹了分佈式事務的概念以及一些解決方案。fenSeata是一款開源的分佈式事務解決方案,致力於提供高性能和簡單易用的分佈式事務服務。Seata將為用戶提供了AT、TCC、SAGA和XA事務模式,為用戶打造一站式的分佈式解決方案。
Seata介紹
本文以一個用戶下單購買商品的系統為例,介紹開源框架Seata的原理和使用,下單該系統涉及三部分服務:
- 倉儲服務:對給定的商品扣除倉儲數量;
- 訂單服務:根據採購需求創建訂單;
- 帳戶服務:從用戶帳戶中扣除餘額;
分佈式事務的主要作用是保證微服務情況下用戶下單過程中數據的一致性。這裡的一致性可以這樣理解:不會出現用戶餘額扣除成功,但是倉儲和訂單相關操作失敗的場景,三者要麼同時成功,要麼同時失敗。
單機事務場景
如果用戶下單購買商品涉及到的服務都在一個傳統的單機服務中,三部分服務可以共享同一個數據庫實例。這種情況下,我們可以通過本地事務的一致性保證倉儲/訂單/賬戶三者之間數據的一致性。
如上圖所示,在單機服務中,三部分內容共用同一個數據庫實例,所以我們只需要本地事務就可以解決數據的一致性,以Spring框架為例,我們只需要在方法上添加@Transaction
註解就可以實現整個購買流程中數據的一致性:
@Transaction
public void purchase(){
doStoreBusiness();
doOrderBusiness();
doAccountBusiness();
}
分佈式事務場景
在微服務框架中,倉儲/訂單/賬戶服務部署在不同的服務器上,使用不同的數據庫實例,與單機模式完全不同。單機模式中的事務通常要求事務涉及的數據源為同一個,並且事務涉及的數據庫操作在同一個數據庫鏈接中,分佈式情況下顯然不滿足條件。
所以在分佈式場景如何保證數據的一致性呢?這就是Seata需要解決的問題了。
Seata解決方案
Seata是用於解決分佈式事務的開源框架,其內部有關於分佈式事務的定義如下:分佈式事務是由多個分支事務組成的全局事務,其中每個分支事務都是本地事務的形式。
Seata框架包含三部分內容:
- 事務協調器(Transaction Coordinator,TC):維護全局事務和分支事務的狀態,進行全局事務提交或全局事務回滾;
- 事務管理器(Transaction Manager,TM):定義全局事務,開啟全局事務、提交全局事務或回滾全局事務;
- 資源管理器(Resource Manager,RM):管理分支事務中的資源,向事務管理器註冊分支事務和並報告分支事務的狀態,負責分支事務提交或回滾;
一個典型的seata分佈式事務的流程如下:
- TM向TC發出開啟全局事務請求,TC生成全局事務的唯一標識XID,設此處的全局事務為T1;
- 在全局事務T1的各個流程中,XID會作為事務的標識在微服務之間流轉;
- RM向TC註冊本地事務,註冊的本地事務的會作為全局事務T1的分支事務;
- TM可以請求TC控制全局事務T1提交或全局事務T1回滾;
- TC可以請求全局事務T1下的所有分支事務提交或回滾;
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的使用方式:
- 倉儲微服務:對給定的商品扣除倉儲數量;
- 訂單微服務:根據採購需求創建訂單;
- 帳戶微服務:從用戶帳戶中扣除餘額;
服務接口的定義
對於三個微服務,我們用三個接口來抽象其內部的邏輯:
-
倉儲服務
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框架,具體工作原理在後續文章中詳細介紹。
我是御狐神,歡迎大家關注我的微信公眾號:wzm2zsd
參考文檔
本文最先發佈至微信公眾號,版權所有,禁止轉載!