天天看點

分布式事務專題(四):分布式事務解決方案之TCC

目錄:

  • 基礎概念
  • 分布式事務理論
  • 分布式事務解決方案之2pc
  • 分布式事務解決方案之TCC(本章)
  • 分布式事務解決方案之可靠消息最終一緻性
  • 分布式事務解決方案之最大努力通知
  • 分布式事務綜合案例分析

4.分布式事務解決方案之TCC 

4.1.什麼是TCC事務

TCC是Try、Confifirm、Cancel三個詞語的縮寫,TCC要求每個分支事務實作三個操作:預處理Try、确認Confifirm、撤銷Cancel。Try操作做業務檢查及資源預留,Confifirm做業務确認操作,Cancel實作一個與Try相反的操作即復原操作。TM首先發起所有的分支事務的try操作,任何一個分支事務的try操作執行失敗,TM将會發起所有分支事務的Cancel操作,若try操作全部成功,TM将會發起所有分支事務的Confifirm操作,其中Confifirm/Cancel操作若執行失敗,TM會進行重試。 

分布式事務專題(四):分布式事務解決方案之TCC

分支事務失敗的情況: 

分布式事務專題(四):分布式事務解決方案之TCC

TCC分為三個階段:

  1. Try 階段是做業務檢查(一緻性)及資源預留(隔離),此階段僅是一個初步操作,它和後續的Confifirm 一起才能真正構成一個完整的業務邏輯。
  2. Confifirm 階段是做确認送出,Try階段所有分支事務執行成功後開始執行 Confifirm。通常情況下,采用TCC則認為 Confifirm階段是不會出錯的。即:隻要Try成功,Confifirm一定成功。若Confifirm階段真的出錯了,需引入重試機制或人工處理。
  3. Cancel 階段是在業務執行錯誤需要復原的狀态下執行分支事務的業務取消,預留資源釋放。通常情況下,采用TCC則認為Cancel階段也是一定成功的。若Cancel階段真的出錯了,需引入重試機制或人工處理。

TM事務管理器,TM事務管理器可以實作為獨立的服務,也可以讓全局事務發起方充當TM的角色,TM獨立出來是為了成為公用元件,是為了考慮系統結構和軟體複用。

TM在發起全局事務時生成全局事務記錄,全局事務ID貫穿整個分布式事務調用鍊條,用來記錄事務上下文,追蹤和記錄狀态,由于Confifirm 和cancel失敗需進行重試,是以需要實作為幂等,幂等性是指同一個操作無論請求多少次,其結果都相同。 

4.2.TCC 解決方案 

目前市面上的TCC架構衆多比如下面這幾種: (以下資料采集日為2019年07月11日) 

架構名稱 Gitbub位址 star數量
tcc-transaction github.com/changmingxi… 3850
Hmily github.com/yu199195/hm… 2407
ByteTCC github.com/liuyangming… 1947
EasyTransaction github.com/QNJR-GROUP/… 1690

上一節所講的Seata也支援TCC,但Seata的TCC模式對Spring Cloud并沒有提供支援。我們的目标是了解TCC的原理以及事務協調運作的過程,是以更請傾向于輕量級易于了解的架構,是以最終确定了Hmily。

Hmily是一個高性能分布式事務TCC開源架構。基于Java語言來開發(JDK1.8),支援Dubbo,Spring Cloud等RPC架構進行分布式事務。它目前支援以下特性: 

  • 支援嵌套事務(Nested transaction support).
  • 采用disruptor架構進行事務日志的異步讀寫,與RPC架構的性能毫無差别。
  • 支援SpringBoot-starter 項目啟動,使用簡單。
  • RPC架構支援 : dubbo,motan,springcloud。
  • 本地事務存儲支援 : redis,mongodb,zookeeper,fifile,mysql。
  • 事務日志序列化支援 :java,hessian,kryo,protostuffff。
  • 采用Aspect AOP 切面思想與Spring無縫內建,天然支援叢集。
  • RPC事務恢複,逾時異常恢複等。 

Hmily利用AOP對參與分布式事務的本地方法與遠端方法進行攔截處理,通過多方攔截,事務參與者能透明的調用到另一方的Try、Confifirm、Cancel方法;傳遞事務上下文;并記錄事務日志,酌情進行補償,重試等。

Hmily不需要事務協調服務,但需要提供一個資料(mysql/mongodb/zookeeper/redis/fifile)來進行日志存儲。

Hmily實作的TCC服務與普通的服務一樣,隻需要暴露一個接口,也就是它的Try業務。Confifirm/Cancel業務邏輯,隻是因為全局事務送出/復原的需要才提供的,是以Confifirm/Cancel業務隻需要被Hmily TCC事務架構發現即可,不需要被調用它的其他業務服務所感覺。 

TCC需要注意三種異常處理分别是空復原、幂等、懸挂: 

空復原:

在沒有調用 TCC 資源 Try 方法的情況下,調用了二階段的 Cancel 方法,Cancel 方法需要識别出這是一個空復原,然後直接傳回成功。

出現原因是當一個分支事務所在服務當機或網絡異常,分支事務調用記錄為失敗,這個時候其實是沒有執行Try階段,當故障恢複後,分布式事務進行復原則會調用二階段的Cancel方法,進而形成空復原。

解決思路的關鍵就是要識别出這個空復原。思路很簡單就是需要知道一階段是否執行,如果執行了,那就是正常復原;如果沒執行,那就是空復原。前面已經說過TM在發起全局事務時生成全局事務記錄,全局事務ID貫穿整個分布式事務調用鍊條。再額外增加一張分支事務記錄表,其中有全局事務 ID 和分支事務 ID,第一階段 Try 方法裡會插入一條記錄,表示一階段執行了。Cancel 接口裡讀取該記錄,如果該記錄存在,則正常復原;如果該記錄不存在,則是空復原。 

幂等:

通過前面介紹已經了解到,為了保證TCC二階段送出重試機制不會引發資料不一緻,要求 TCC 的二階段 Try、Confifirm 和 Cancel 接口保證幂等,這樣不會重複使用或者釋放資源。如果幂等控制沒有做好,很有可能導緻資料不一緻等嚴重問題。

解決思路在上述“分支事務記錄”中增加執行狀态,每次執行前都查詢該狀态。 

懸挂:

懸挂就是對于一個分布式事務,其二階段 Cancel 接口比 Try 接口先執行。

出現原因是在 RPC 調用分支事務try時,先注冊分支事務,再執行RPC調用,如果此時 RPC 調用的網絡發生擁堵,通常 RPC 調用是有逾時時間的,RPC 逾時以後,TM就會通知RM復原該分布式事務,可能復原完成後,RPC 請求才到達參與者真正執行,而一個 Try 方法預留的業務資源,隻有該分布式事務才能使用,該分布式事務第一階段預留的業務資源就再也沒有人能夠處理了,對于這種情況,我們就稱為懸挂,即業務資源預留後沒法繼續處理。

解決思路是如果二階段執行完成,那一階段就不能再繼續執行。在執行一階段事務時判斷在該全局事務下,“分支事務記錄”表中是否已經有二階段事務記錄,如果有則不執行Try。 

舉例,場景為 A 轉賬 30 元給 B,A和B賬戶在不同的服務。

方案1: 

賬戶A

try:
      檢查餘額是否夠30元 
      扣減30元 
confirm: 
     空 
cancel:
    增加30元
           

賬戶B

try:   
   增加30元 
confirm:    
   空 
cancel:   
   減少30元
           

方案1說明:

1)賬戶A,這裡的餘額就是所謂的業務資源,按照前面提到的原則,在第一階段需要檢查并預留業務資源,是以,我們在扣錢 TCC 資源的 Try 接口裡先檢查 A 賬戶餘額是否足夠,如果足夠則扣除 30 元。 Confifirm 接口表示正式送出,由于業務資源已經在 Try 接口裡扣除掉了,那麼在第二階段的 Confifirm 接口裡可以什麼都不用做。Cancel接口的執行表示整個事務復原,賬戶A復原則需要把 Try 接口裡扣除掉的 30 元還給賬戶。

2)賬号B,在第一階段 Try 接口裡實作給賬戶B加錢,Cancel 接口的執行表示整個事務復原,賬戶B復原則需要把Try 接口裡加的 30 元再減去。 

方案1的問題分析: 

  1. 如果賬戶A的try沒有執行在cancel則就多加了30元。
  2. 由于try,cancel、confifirm都是由單獨的線程去調用,且會出現重複調用,是以都需要實作幂等。
  3. 賬号B在try中增加30元,當try執行完成後可能會其它線程給消費了。
  4. 如果賬戶B的try沒有執行在cancel則就多減了30元。 

問題解決: 

  1. 賬戶A的cancel方法需要判斷try方法是否執行,正常執行try後方可執行cancel。
  2. try,cancel、confifirm方法實作幂等。
  3. 賬号B在try方法中不允許更新賬戶金額,在confifirm中更新賬戶金額。
  4. 賬戶B的cancel方法需要判斷try方法是否執行,正常執行try後方可執行cancel。

優化方案: 

賬戶A

try:   
   try幂等校驗    
   try懸挂處理    
   檢查餘額是否夠30元    
   扣減30元 
confirm:    
   空 
cancel:   
   cancel幂等校驗    
   cancel空復原處理    
   增加可用餘額30元
           

賬戶B

try:   
   空 
confirm:    
   confirm幂等校驗    
   正式增加30元 
cancel:   
   空
           

最後

後面的内容,會按章節更新的,可以關注我持續閱讀,覺得不錯可以點個贊支援一下!