您好,登錄后才能下訂單哦!
這篇文章主要講解了“怎么用python完成一個(gè)分布式事務(wù)TCC”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“怎么用python完成一個(gè)分布式事務(wù)TCC”吧!
TCC分為3個(gè)階段
Try 階段:嘗試執(zhí)行,完成所有業(yè)務(wù)檢查(一致性), 預(yù)留必須業(yè)務(wù)資源(準(zhǔn)隔離性)
Confirm 階段:如果所有分支的Try都成功了,則走到Confirm
階段。Confirm
真正執(zhí)行業(yè)務(wù),不作任何業(yè)務(wù)檢查,只使用 Try 階段預(yù)留的業(yè)務(wù)資源
Cancel 階段:如果所有分支的Try有一個(gè)失敗了,則走到Cancel
階段。Cancel
釋放 Try
階段預(yù)留的業(yè)務(wù)資源。
TCC分布式事務(wù)里,有3個(gè)角色,與經(jīng)典的XA分布式事務(wù)一樣:
AP/應(yīng)用程序,發(fā)起全局事務(wù),定義全局事務(wù)包含哪些事務(wù)分支
RM/資源管理器,負(fù)責(zé)分支事務(wù)各項(xiàng)資源的管理
TM/事務(wù)管理器,負(fù)責(zé)協(xié)調(diào)全局事務(wù)的正確執(zhí)行,包括Confirm
,Cancel
的執(zhí)行,并處理網(wǎng)絡(luò)異常
如果我們要進(jìn)行一個(gè)類似于銀行跨行轉(zhuǎn)賬的業(yè)務(wù),轉(zhuǎn)出(TransOut
)和轉(zhuǎn)入(TransIn
)分別在不同的微服務(wù)里,
一個(gè)成功完成的TCC事務(wù)典型的時(shí)序圖如下:
對(duì)于前面的跨行轉(zhuǎn)賬操作,最簡(jiǎn)單的做法是,在Try階段調(diào)整余額,在Cancel階段反向調(diào)整余額,Confirm
階段則空操作。這么做帶來的問題是,如果A扣款成功,金額轉(zhuǎn)入B失敗,最后回滾,把A的余額調(diào)整為初始值。在這個(gè)過程中如果A發(fā)現(xiàn)自己的余額被扣減了,但是收款方B遲遲沒有收到余額,那么會(huì)對(duì)A造成困擾。
更好的做法是,Try階段凍結(jié)A轉(zhuǎn)賬的金額,Confirm
進(jìn)行實(shí)際的扣款,Cancel
進(jìn)行資金解凍,這樣用戶在任何一個(gè)階段,看到的數(shù)據(jù)都是清晰明了的。
下面我們進(jìn)行一個(gè)TCC事務(wù)的具體開發(fā)
目前可用于TCC的開源框架,主要為Java語言,其中以seata
為代表。我們的例子采用Python
語言,使用的分布式事務(wù)框架為 https://github.com/yedf/dtm ,它對(duì)分布式事務(wù)的支持非常優(yōu)雅。下面來詳細(xì)講解TCC
的組成
我們首先創(chuàng)建兩張表,一張是用戶余額表,一張是凍結(jié)資金表,建表語句如下:
CREATE TABLE dtm_busi.`user_account` ( `id` int(11) AUTO_INCREMENT PRIMARY KEY, `user_id` int(11) not NULL UNIQUE , `balance` decimal(10,2) NOT NULL DEFAULT '0.00', `create_time` datetime DEFAULT now(), `update_time` datetime DEFAULT now() ); CREATE TABLE dtm_busi.`user_account_trading` ( `id` int(11) AUTO_INCREMENT PRIMARY KEY, `user_id` int(11) not NULL UNIQUE , `trading_balance` decimal(10,2) NOT NULL DEFAULT '0.00', `create_time` datetime DEFAULT now(), `update_time` datetime DEFAULT now() );
trading
表中,trading_balance
記錄正在交易的金額。
我們先編寫核心代碼,凍結(jié)/解凍資金操作,會(huì)檢查約束balance+trading_balance >= 0
,如果約束不成立,執(zhí)行失敗
def tcc_adjust_trading(cursor, uid, amount): affected = utils.sqlexec(cursor, "update dtm_busi.user_account_trading set trading_balance=trading_balance + %d where user_id=%d and trading_balance + %d + (select balance from dtm_busi.user_account where id=%d) >= 0" % (amount, uid, amount, uid)) if affected == 0: raise Exception("update error, maybe balance not enough")
然后是調(diào)整余額
def tcc_adjust_balance(cursor, uid, amount): utils.sqlexec(cursor, "update dtm_busi.user_account_trading set trading_balance = trading_balance+ %d where user_id=%d" %( -amount, uid)) utils.sqlexec(cursor, "update dtm_busi.user_account set balance=balance+%d where user_id=%d" %(amount, uid))
下面我們來編寫具體的Try/Confirm/Cancel
的處理函數(shù)
@app.post("/api/TransOutTry") def trans_out_try(): # 事務(wù)以及異常處理 tcc_adjust_trading(c, out_uid, -30) return {"dtm_result": "SUCCESS"} @app.post("/api/TransOutConfirm") def trans_out_confirm(): # 事務(wù)以及異常處理 tcc_adjust_balance(c, out_uid, -30) return {"dtm_result": "SUCCESS"} @app.post("/api/TransOutCancel") def trans_out_cancel(): # 事務(wù)以及異常處理 tcc_adjust_trading(c, out_uid, 30) return {"dtm_result": "SUCCESS"} @app.post("/api/TransInTry") def trans_in_try(): # 事務(wù)以及異常處理 tcc_adjust_trading(c, in_uid, 30) return {"dtm_result": "SUCCESS"} @app.post("/api/TransInConfirm") def trans_in_confirm(): # 事務(wù)以及異常處理 tcc_adjust_balance(c, in_uid, 30) return {"dtm_result": "SUCCESS"} @app.post("/api/TransInCancel") def trans_in_cancel(): # 事務(wù)以及異常處理 tcc_adjust_trading(c, in_uid, -30) return {"dtm_result": "SUCCESS"}
到此各個(gè)子事務(wù)的處理函數(shù)已經(jīng)OK了,然后是開啟TCC事務(wù),進(jìn)行分支調(diào)用
@app.get("/api/fireTcc") def fire_tcc(): # 發(fā)起tcc事務(wù) gid = tcc.tcc_global_transaction(dtm, utils.gen_gid(dtm), tcc_trans) return {"gid": gid} # tcc事務(wù)的具體處理 def tcc_trans(t): req = {"amount": 30} # 業(yè)務(wù)請(qǐng)求的負(fù)荷 # 調(diào)用轉(zhuǎn)出服務(wù)的Try|Confirm|Cancel t.call_branch(req, svc + "/TransOutTry", svc + "/TransOutConfirm", svc + "/TransOutCancel") # 調(diào)用轉(zhuǎn)入服務(wù)的Try|Confirm|Cancel t.call_branch(req, svc + "/TransInTry", svc + "/TransInConfirm", svc + "/TransInCancel")
至此,一個(gè)完整的TCC
分布式事務(wù)編寫完成。
如果您想要完整運(yùn)行一個(gè)成功的示例,那么按照dtmcli-py-sample
項(xiàng)目的說明tcc的例子即可
假如銀行將金額準(zhǔn)備轉(zhuǎn)入用戶2時(shí),發(fā)現(xiàn)用戶2的賬戶異常,返回失敗,會(huì)怎么樣?我們修改代碼,模擬這種情況:
@app.post("/api/TransInTry") def trans_in_try(): # 事務(wù)以及異常處理 tcc_adjust_trading(c, in_uid, 30) return {"dtm_result": "FAILURE"}
這是事務(wù)失敗交互的時(shí)序圖:
這個(gè)跟成功的TCC差別就在于,當(dāng)某個(gè)子事務(wù)返回失敗后,后續(xù)就回滾全局事務(wù),調(diào)用各個(gè)子事務(wù)的Cancel
操作,保證全局事務(wù)全部回滾。
TCC
在整個(gè)全局事務(wù)的過程中,可能發(fā)生各類網(wǎng)絡(luò)異常情況,典型的是空回滾、冪等、懸掛,由于TCC
的異常情況,和SAGA
、可靠消息等事務(wù)模式有相近的地方,因此我們把所有異常的解決方案統(tǒng)統(tǒng)放在這篇文章 分布式事務(wù)最經(jīng)典的七種解決方案 的異常處理章節(jié)進(jìn)行講解
感謝各位的閱讀,以上就是“怎么用python完成一個(gè)分布式事務(wù)TCC”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)怎么用python完成一個(gè)分布式事務(wù)TCC這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。