天天看點

seata-golang 接入指南AT 模式接入TCC 模式接入總結陳述

seata-golang 接入指南AT 模式接入TCC 模式接入總結陳述

作者 | 劉曉敏

來源|阿裡巴巴雲原生公衆号

seata-golang 是一個分布式事務架構,實作了 AT 模式和 TCC 模式,AT 模式相較 TCC 模式對代碼的入侵性更小、需要開發的接口更少;但 AT 模式對事務操作的資料持有全局鎖,從這點來說,TCC 模式性能更好。

seata 的 AT 模式将全局鎖放在 transaction coordinator 也就是事務協調器上,依賴于具體鎖接口的存儲實作方式可以是 file/db/redis 等,而不是資料庫鎖,每個分支事務送出時立即釋放資料庫鎖,這樣對資料庫的壓力也就減小了,變相得提升了資料庫的性能。seata AT 模式和 TCC 模式的原理見:[Seata 是什麼?]

下面以 seats-golang samples 為例,就 AT 模式和 TCC 模式如何接入到業務中做一個說明。

在 samples/at 目錄下,有三個微服務:product_svc、order_svc、aggregation_svc。

product_svc 負責建立訂單時扣減庫存。

order_svc 負責建立訂單時寫入訂單主表和訂單明細表。

aggregation_svc 通過 http 請求調用 order_svc 和 product _svc 的接口。

seata-golang 接入指南AT 模式接入TCC 模式接入總結陳述

熟悉 seata java 架構的都知道,seata java 架構通過掃描 @GlobalTransactional 注解,動态生成 AOP 切面,代理被 @GlobalTransactional 标記的方法,實作全局事務的開啟、送出或者復原。

不同于作為解釋型語言的 Java,Go 是一種編譯型語言,是以 seata-golang 使用了反射技術實作動态代理功能,被代理的對象需要實作 GlobalTransactionProxyService 接口。

aggregation_svc 中的 Svc struct 有一個方法 <code>CreateSo</code>,該方法通過對 order_svc 和 product_svc 的調用實作了建立訂單和扣減庫存。seata-golang 要代理該 *Svc 對象,需要建立一個代理對象,被代理的方法要在代理對象中作為一個空方法成員,等待 seata-golang 去動态實作。

代理對象 ProxyService 通過組合方式内置被代理對象 Svc,在開發者調用 <code>tm.Implement(svc.ProxySvc)</code> 方法後,seata-golang 會通過 Svc 實作的 GlobalTransactionProxyService 接口擷取動态建立 CreateSo 方法所需要的事務資訊,然後根據這些事務資訊去動态建立 CreateSo 方法:開啟事務 -&gt; 執行被代理 *Svc 對象的 CreateSo 方法邏輯 -&gt; 根據被代理的 CreateSo 方法的傳回錯誤資訊決定送出還是復原。

seata-golang 接入指南AT 模式接入TCC 模式接入總結陳述

可以通過如下三種方式傳遞全局事務 ID。

在 aggregation_svc 這個服務裡,Seata-golang 通過 request header (<code>req.Header.Set("XID", rootContext.GetXID())</code>)将 XID (全局事務 ID)傳遞到了 order_svc 和 product_svc,order_svc 和 product_svc 則從 Request Header 取出 XID (<code>c.Request.Header.Get("XID")</code>)用于分支事務處理。

如果使用 dubbo 協定 rpc 通信,則需要把 XID 注入到 attachment 中傳遞到下遊。

seata-golang 接入指南AT 模式接入TCC 模式接入總結陳述

如果使用 dubbo-go 架構,dubbo-go 會從 context 中讀取 attachment 将其序列化傳遞給服務端。可以采用如下的方式,将 XID 傳遞出去:

dubbo-go 服務端則從 attachment 中取出 XID,再注入到 context 中,分支事務的業務方法則可以從 context 中擷取 XID 用于分支事務處理。

上面的 filter 通過 <code>extension.SetFilter("SEATA", getSeataFilter)</code> 方法可将其注入到 dubbo-go 的 filter 鍊。

grpc 可通過 metadata 傳遞 XID。

用戶端首先将 XID 放入 <code>md := metadata.Pairs("XID", rootContext.GetXID())</code>,再将 metadata 傳入 context:<code>metadata.NewOutgoingContext(context.Background(), md)</code>。

服務端則通過 <code>md, ok := metadata.FromIncomingContext(ctx)</code> 擷取到 metadata,再從中取出 XID。

AT 模式除了要對發起全局事務的方法做代理,還需要對資料源做代理。

seata 通過代理資料源,對 sql 語句進行解析,來擷取修改資料的修改前和修改後的資料,供 transaction coordinator 復原時使用。對資料源的代理,隻需要将你建立的 sql driver 執行個體注入到 seata-golang 的 db 操作對象中:

如果你使用了 xorm 或者 gorm,則可從 xorm 對象或者 gorm 對象中取出 sql driver 執行個體,用上面的方法構造出 seata-golang 的 db 操作對象。這意味着你可以同時使用 orm 架構和 seata-golang 架構,當你的操作需要用到事務時,用 seata-golang 的 db 操作對象去執行 sql 語句。

通過上一節的介紹,開發者已經可以在服務端拿到上遊傳遞過來的 XID 了。為了将分支事務加入到全局事務組中,開發者需要使用擷取的 XID 構造一個 RootContext:

開啟分支事務時,調用流程如下:

調用 seata-golang 的 db 操作對象的 Begin 方法擷取分支事務對象 <code>tx, err := dao.Begin(ctx)</code>。

執行 sql 語句則使用該分支事務對象 tx 的 Exec 方法 <code>func (tx *Tx) Exec(query string, args ...interface{}) (sql.Result, error)</code>。

執行完 sql 操作邏輯後,可根據傳回的結果,調用 <code>tx.Commit()</code> 或 <code>tx.Rollback()</code> 來送出或復原分支操作。

最後,整個分支事務是否成功送出,執行成功還是失敗需要傳回結果給調用方,也就是全局事務的發起方,transaction manager 會根據傳回的結果決定是否送出或復原整個全局事務。

TCC 模式相較 AT 模式,限制會多一些。TCC 模式首先要求開發者實作 TccService 接口,還要求接口三個方法的參數都封裝到一個 BusinessActionContext 裡。

開發者調用 Try 方法,seata-golang 架構調用 Confirm/Cancel 方法。架構根據所有分支事務 Try 方法是否都執行成功,來決定發起全局送出或復原。全局送出則由架構自動調用每個事務分支的 Confirm 方法,全局復原則調用加入事務組的所有事務分支的 Cancel 方法。

在調用 Try 方法之前,事務分支要加入事務組,且需要把 Try 方法執行的上下文即 BusinessActionContext 存到 Transaction coordinator,這樣架構在送出或復原時,才能把 BusinessActionContext 參數傳遞給 confirm、cancel 方法,這部分邏輯仍然通過代理實作。是以開發者還需要建立一個代理類,并實作接口 TccProxyService:

通過調用 <code>tcc.ImplementTCC({代理類執行個體})</code> 方法,架構會為代理類實作上述邏輯。開發者可在 samples/tcc 目錄檢視 tcc 模式的示例。

seata-golang 接入指南AT 模式接入TCC 模式接入總結陳述

除了項目結構目錄内的 samples,還有一個 dubbo-go 的例子 dubbo-go-seata。對于上文講述的接入方法,還希望讀者結合代碼多多了解,融彙貫通。

目前 seata-golang 與最新的 seata java 1.4 版本協定上完全打通,如果有公司在技術棧上既有使用 java 語言也有使用 golang 語言,可接入 seata 架構來解決您的分布式事務後顧之憂。

seata 官方:https://seata.io

java 版 seata:https://github.com/seata/seata

seata-golang 項目位址:https://github.com/opentrx/seata-golang

seata-golang go 夜讀 b 站分享:https://www.bilibili.com/video/BV1oz411e72T

基于 getty 的 seata-golang 通信模型詳解:http://seata.io/zh-cn/blog/seata-golang-communication-mode.html

劉曉敏 (GitHubID dk-lockdown),目前就職于 h3c 成都分公司,擅長使用 Java/Go 語言,在雲原生和微服務相關技術方向均有涉獵,目前專攻分布式事務。