天天看點

DTM在新交易平台的落地 | 業務平台

作者:閃念基因

一、項目背景

在項目的發展過程中,我們将整個新交易平台(業務平台部專門為360集團内部打造的類似有贊、微盟的交易系統)微服務化,産生了店鋪服務、商品服務、訂單服務、優惠券服務、紅包服務、使用者服務、支付服務、履約服務、售後服務等等。并且這些服務由不同的語言開發。

目前端的使用者送出訂單,服務端需要完成以下操作:

  • 建立訂單:需要在訂單表中建立訂單,唯一鍵為訂單ID
  • 扣減庫存:需要給使用者下單的商品扣減庫存
  • 核銷優惠券:使用者在下單前,選擇了可使用的優惠券,送出訂單時,則扣減這部分優惠券
  • 扣減紅包:使用者在下單前,選擇了可使用的紅包,送出訂單時,則扣減這部分紅包金額
  • 扣減積分:使用者在下單前,如果有積分餘額,送出訂單時,則扣減這部分積分
  • 建立支付單:送出訂單後,需要建立支付單,最終供使用者支付

對于上述這個場景,如果在單體訂單系統中,很容易使用資料庫的事務來解決。但是一旦微服務化了之後,由于這些操作分布在不同的服務中間,則需要按順序依次去調用各個過程。在這個過程中就會遇到許多程序故障、某一操作無法完成需復原、重複請求等問題。是以我們就要考慮分布式場景下的事務一緻性解決方案。

二、分布式方案選型

由于訂單建立的過程中是需要復原的,是以首先排除了消息通知類(消息通知、本地事務消息等)的模式。排除之後發現僅剩XA、TCC、SAGA,下面我們分析一下這三種事務模式

XA

XA協定最初由Tuxedo首先提出,後被送出給X/Open組織作為資料總管(資料庫)與事務管理器的接口标準。XA規範主要定義了全局事務管理器(TM)和局部資料總管(RM)之間的接口。在XA中,本地的資料庫扮演的是RM角色。目前,主流的資料庫基本都支援XA事務,包括MySQL、Oracle、SQL Server和PostgreSQL。

XA一共分為兩階段:

  • 第一階段(prepare):事務管理器(TM)會向所有參與者發送prepare消息,詢問它們是否可以送出事務
  • 第二階段 (commit/rollback):如果所有參與者都能夠送出,則事務管理器(TM)向它們發送commit消息,讓它們送出事務。如果有任何一個參與者不能送出,則協調器向它們發送rollback消息,讓它們復原事務。
DTM在新交易平台的落地 | 業務平台

通過這兩個階段操作,使所有的參與者資料都能保持一緻性,但XA事務模式雖然能夠保證分布式事務的一緻性,但是由于是資料庫層面的資源鎖定,會帶來一些性能上的開銷。排除此模式

TCC

TCC是Try、Confirm、Cancel三個詞語的縮寫,最早是由 Pat Helland 于 2007 年發表的一篇名為《Life beyond Distributed Transactions:an Apostate’s Opinion》的論文提出。

TCC分為3個階段

  • Try 階段:嘗試執行,完成所有業務檢查(一緻性), 預留必須業務資源(準隔離性)
  • Confirm 階段:如果所有分支的Try都成功了,則走到Confirm階段。Confirm真正執行業務,不作任何業務檢查,隻使用 Try 階段預留的業務資源
  • Cancel 階段:如果所有分支的Try有一個失敗了,則走到Cancel階段。Cancel釋放 Try 階段預留的業務資源。

TCC特點如下:

  • 并發度較高,無長期資源鎖定
  • 開發量較大,需要提供Try/Confirm/Cancel接口
  • 一緻性較好,不會有暴漏給使用者待支付訂單,最後又被取消的情況
  • TCC适用于訂單類業務,對中間狀态有限制的業務

但是由于我們系統存在曆史邏輯,短時間内無法完全重構以支援資源預留,是以也排除了此模式

SAGA

SAGA最初出現在1987年Hector Garcaa-Molrna & Kenneth Salem發表的論文SAGAS裡。其核心思想是将長事務拆分為多個短事務,由Saga事務協調器協調,如果每個短事務都成功送出完成,那麼全局事務就正常完成,如果某個步驟失敗,則根據相反順序一次調用補償操作。

Saga分為2個階段

  • Action階段:正向執行,無需做資源預留
  • Compensate階段:如果某一個過程出錯,調用補償接口,依次進行資源逆向補償

Saga事務的特點:

  • 并發度高,不用像XA事務那樣長期鎖定資源
  • 需要定義正常操作以及補償操作,開發量比XA大,但比TCC小
  • 一緻性較弱,對于訂單建立,有可能出現待支付訂單,最後又被取消的情況

由于Saga僅需要在發生異常需復原時提供一個逆向補償的接口,開發量較少,且幾乎不用對曆史業務邏輯進行改動。故采用此方案而接受短暫資料不一緻的問題。

三、SAGA模式&DTM在建立訂單過程中的實踐

1、DTM分布式事務管理器的确認

确定了Saga模式,接着我們就要進行實施落地,如果自研分布式事務管理器有很大的開發工作,于是去尋找有沒有符合我們實際需求的開源工具,最終确定兩款比較成熟的開源系統,JAVA SEATA&&Golang DTM,由于seata僅支援Dubbo、Spring Cloud等協定,并且除java版本SDK外,其他語言的SDK完善度不高。DTM支援http、grpc協定。最終确定使用DTM。

接入DTM後的下單時序如下所示:

DTM在新交易平台的落地 | 業務平台

首先看看,下單 api 的主要處理過程:

DTM在新交易平台的落地 | 業務平台

上面的代碼首先建立了一個SAGA事務,然後添加了多個子事務,每個事務分支包括action和compensate兩個操作,分别為Add函數的第一第二個參數。子事務定好之後送出給dtm。dtm收到saga送出的全局事務後,會調用所有子事務的正向操作,如果所有正向操作成功完成,那麼事務成功結束。如果有正向操作失敗,例如賬戶庫存不足,那麼dtm會調用各分支的補償操作,進行復原,最後事務成功復原。

  • 程序crash問題 dtm的saga事務進行過程中,如果發生程序crash,那麼dtm會進行重試,保證操作會最終完成
  • 復原問題 上述這個saga事務中,如果扣減庫存時發現庫存不足,則傳回failure,會進行復原。dtm 會記錄哪些操作已完成,并復原相關的操作

但是由于分布式事務的引入,因為網絡的時序無法保證,會引入幂等、空復原、懸挂等新問題。

2、解決重複請求、空復原、懸挂等問題

分布式事務之是以難,主要是因為分布式系統中的各個節點都可能發生各種非預期的情況。分布式系統最大的敵人可能就是NPC了,在這裡它是Network Delay, Process Pause, Clock Drift的首字母縮寫。我們先看看具體的NPC問題是什麼:

  • Network Delay,網絡延遲。雖然網絡在多數情況下工作的還可以,雖然TCP保證傳輸順序和不會丢失,但它無法消除網絡延遲問題。
  • Process Pause,程序暫停。當基于某些需要,例如記憶體垃圾回收、CPU 排隊、服務遷移等,某服務會暫時暫停。
  • Clock Drift,時鐘漂移。分布式系統涉及大量的伺服器,而不同伺服器通常使用 NTP (Network Time Protocol)協定将本地裝置的時間與時間伺服器對齊對齊後,通常會導緻本地時間跳躍。

分布式事務既然是分布式的系統,自然也有NPC問題。因為沒有涉及時間戳,帶來的困擾主要是NP。我們以扣減庫存為例,看看NP帶來的影響

3、網絡異常狀态下庫存扣減問題

DTM在新交易平台的落地 | 業務平台

一般情況下,一個SAGA異步補償時的執行順序是,先執行完庫存扣減,再執行庫存復原,但是由于N,則有可能庫存扣減的網絡延遲大,導緻先執行庫存復原,再執行庫存扣減。

這種情況就引入了分布式事務中的兩個難題:

  • 空補償:compensate執行時,action未執行,事務分支的compensate操作需要判斷出action未執行,這時需要忽略compensate中的業務資料更新,直接傳回
  • 懸挂:action執行時,compensate已執行完成,事務分支的action操作需要判斷出compensate已執行,這時需要忽略action中的業務資料更新,直接傳回

分布式事務還有一類需要處理的常見問題,就是重複請求

  • 幂等: 由于任何一個請求都可能出現網絡異常,出現重複請求,所有的分布式事務分支操作,都需要保證幂等性

上圖中,庫存扣減操作逾時而執行的庫存復原,若在商品服務執行成功,但回報的結果由于 NPC 問題不能到達事務排程器,那麼事務排程器還有可能再次發送庫存復原。這就意味着商品服務的庫存復原操作會被多次重複調用。我們必須保證分布式事務的全部操作分支保證幂等性。也就是重複調用操作分支,但不會産生疊加的影響。

不論空補償、懸挂還是幂等,都需要在業務邏輯層面做出判定。通常的做法是通過分布式事務事件日志的方案來辨別操作狀态,進而決定是否需要處理空補償和防止懸挂。

4、DTM解決方案在庫存扣減服務中的實作

如果沒有一個通用的操作方法去解決幂等、懸挂、空復原等問題,系統也面臨着大量的開發工作,并且每個業務都要仔細處理這三張問題。好在看到了DTM的子事務屏障技術。

子事務屏障技術的原理是,在本地資料庫,建立分支操作狀态表dtm_barrier,唯一鍵為全局事務id-分支id-分支操作

  • 開啟本地事務
  • 對于目前操作op(action|compensate),insert ignore一條資料gid-branchid-op,如果插入不成功,送出事務傳回成功(常見的幂等控制方法)
  • 如果目前操作是compensate,那麼在insert ignore一條資料gid-branchid-action,如果插入成功(注意是成功),則送出事務傳回成功
  • 調用屏障内的業務邏輯,如果業務傳回成功,則送出事務傳回成功;如果業務傳回失敗,則復原事務傳回失敗

由于我們的庫存服務使用的是java語言,DTM中java版本SDK還不支援SAGA事務的子事務屏障,于是我們使用此原理自己研發

DTM在新交易平台的落地 | 業務平台

在此機制下,我們看一下怎麼解決亂序相關的問題

  • 空補償控制:如果action沒有執行,直接執行了compensate,那麼插入gid-branchid-action會成功,不走屏障内的邏輯,保證了空補償控制
  • 幂等控制:gid-branchid-{action/compensate}在任何一個操作都無法重複插入唯一鍵,保證了不會重複執行
  • 防懸挂控制:action在compensate之後執行,由于執行compensate時會在插入gid-branchid-action,導緻action請求插入gid-branchid-action不成功,就不執行屏障内的邏輯,保證了防懸挂控制

總結

伴随着業務的快速地發展、越來越高的業務複雜度,幾乎每個公司的系統都會從單體走向分布式,特别是轉向微服務架構。分布式事務本身就是一個技術難題,如果沒有合适的架構、工具,分布式事務會大大的提高流程的複雜度,會帶來很多額外的開銷工作。經過我們調研和探索,很好地利用DTM事務管理器與系統結合。将分布式事務相關邏輯全部交由 DTM處理,而讓我們相應的開發者更聚焦于業務本身,隻需要安心寫好相關操作和補償操作即可。

作者:技術中台-楊路超

來源:微信公衆号:360技術工程

出處:https://mp.weixin.qq.com/s/iG9eKnZEdYCIVriktiXdYw

繼續閱讀