一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

腳本之家,腳本語言編程技術及教程分享平臺!
分類導航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務器之家 - 腳本之家 - Python - 用python完成一個分布式事務TCC

用python完成一個分布式事務TCC

2022-02-17 00:23yedf Python

這篇文章主要介紹了用python完成一個分布式事務TCC,文章里我們介紹了TCC的理論知識,也通過一個例子,完整給出了編寫一個TCC事務的過程,涵蓋了正常成功完成,以及成功回滾的情況,需要的朋友可以參考一下文章的具體內容

前言:

什么是分布式事務?銀行跨行轉賬業務是一個典型分布式事務場景,假設A需要跨行轉賬給B,那么就涉及兩個銀行的數據,無法通過一個數據庫的本地事務保證轉賬的ACID,只能夠通過分布式事務來解決。

分布式事務就是指事務的發起者、資源及資源管理器和事務協調者分別位于分布式系統的不同節點之上。在上述轉賬的業務中,用戶A-100操作和用戶B+100操作不是位于同一個節點上。本質上來說,分布式事務就是為了保證在分布式場景下,數據操作的正確執行。

什么是TCC分布式事務,TCCTry、ConfirmCancel三個詞語的縮寫,最早是由 Pat Helland 于 2007 年發表的一篇名為《Life beyond Distributed Transactions:an Apostate"s Opinion》的論文提出。

1、TCC組成

TCC分為3個階段

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

TCC分布式事務里,有3個角色,與經典的XA分布式事務一樣:

  • AP/應用程序,發起全局事務,定義全局事務包含哪些事務分支
  • RM/資源管理器,負責分支事務各項資源的管理
  • TM/事務管理器,負責協調全局事務的正確執行,包括Confirm,Cancel的執行,并處理網絡異常

如果我們要進行一個類似于銀行跨行轉賬的業務,轉出(TransOut)和轉入(TransIn)分別在不同的微服務里,

一個成功完成的TCC事務典型的時序圖如下:

用python完成一個分布式事務TCC

2、TCC實踐

對于前面的跨行轉賬操作,最簡單的做法是,在Try階段調整余額,在Cancel階段反向調整余額,Confirm階段則空操作。這么做帶來的問題是,如果A扣款成功,金額轉入B失敗,最后回滾,把A的余額調整為初始值。在這個過程中如果A發現自己的余額被扣減了,但是收款方B遲遲沒有收到余額,那么會對A造成困擾。

更好的做法是,Try階段凍結A轉賬的金額,Confirm進行實際的扣款,Cancel進行資金解凍,這樣用戶在任何一個階段,看到的數據都是清晰明了的。

下面我們進行一個TCC事務的具體開發

目前可用于TCC的開源框架,主要為Java語言,其中以seata為代表。我們的例子采用Python語言,使用的分布式事務框架為 https://github.com/yedf/dtm ,它對分布式事務的支持非常優雅。下面來詳細講解TCC的組成

我們首先創建兩張表,一張是用戶余額表,一張是凍結資金表,建表語句如下:

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記錄正在交易的金額。

我們先編寫核心代碼,凍結/解凍資金操作,會檢查約束balance+trading_balance >= 0,如果約束不成立,執行失敗

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")


然后是調整余額

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的處理函數

@app.post("/api/TransOutTry")
def trans_out_try():
  # 事務以及異常處理
  tcc_adjust_trading(c, out_uid, -30)
  return {"dtm_result": "SUCCESS"}

@app.post("/api/TransOutConfirm")
def trans_out_confirm():
  # 事務以及異常處理
  tcc_adjust_balance(c, out_uid, -30)
  return {"dtm_result": "SUCCESS"}

@app.post("/api/TransOutCancel")
def trans_out_cancel():
  # 事務以及異常處理
  tcc_adjust_trading(c, out_uid, 30)
  return {"dtm_result": "SUCCESS"}

@app.post("/api/TransInTry")
def trans_in_try():
  # 事務以及異常處理
  tcc_adjust_trading(c, in_uid, 30)
  return {"dtm_result": "SUCCESS"}

@app.post("/api/TransInConfirm")
def trans_in_confirm():
  # 事務以及異常處理
  tcc_adjust_balance(c, in_uid, 30)
  return {"dtm_result": "SUCCESS"}

@app.post("/api/TransInCancel")
def trans_in_cancel():
  # 事務以及異常處理
  tcc_adjust_trading(c, in_uid, -30)
  return {"dtm_result": "SUCCESS"}

到此各個子事務的處理函數已經OK了,然后是開啟TCC事務,進行分支調用

@app.get("/api/fireTcc")
def fire_tcc():
    # 發起tcc事務
    gid = tcc.tcc_global_transaction(dtm, utils.gen_gid(dtm), tcc_trans)
    return {"gid": gid}

# tcc事務的具體處理
def tcc_trans(t):
    req = {"amount": 30} # 業務請求的負荷
    # 調用轉出服務的Try|Confirm|Cancel
    t.call_branch(req, svc + "/TransOutTry", svc + "/TransOutConfirm", svc + "/TransOutCancel")
    # 調用轉入服務的Try|Confirm|Cancel
    t.call_branch(req, svc + "/TransInTry", svc + "/TransInConfirm", svc + "/TransInCancel")

至此,一個完整的TCC分布式事務編寫完成。

如果您想要完整運行一個成功的示例,那么按照dtmcli-py-sample項目的說明tcc的例子即可

3、TCC的回滾

假如銀行將金額準備轉入用戶2時,發現用戶2的賬戶異常,返回失敗,會怎么樣?我們修改代碼,模擬這種情況:

@app.post("/api/TransInTry")
def trans_in_try():
  # 事務以及異常處理
  tcc_adjust_trading(c, in_uid, 30)
  return {"dtm_result": "FAILURE"}


這是事務失敗交互的時序圖:

用python完成一個分布式事務TCC

這個跟成功的TCC差別就在于,當某個子事務返回失敗后,后續就回滾全局事務,調用各個子事務的Cancel操作,保證全局事務全部回滾。

4、TCC網絡異常

TCC在整個全局事務的過程中,可能發生各類網絡異常情況,典型的是空回滾、冪等、懸掛,由于TCC的異常情況,和SAGA、可靠消息等事務模式有相近的地方,因此我們把所有異常的解決方案統統放在這篇文章 分布式事務最經典的七種解決方案 的異常處理章節進行講解

5、小結

在這篇文章里,我們介紹了TCC的理論知識,也通過一個例子,完整給出了編寫一個TCC事務的過程,涵蓋了正常成功完成,以及成功回滾的情況。相信讀者通過這邊文章,對TCC已經有了深入的理解。

關于分布式事務更多更全面的知識,請參考 分布式事務最經典的七種解決方案

文中使用的例子節選自 yedf/dtm ,支持多種事務模式:TCC、SAGA、XA、事務消息 跨語言支持,已支持 golang、python、PHP、nodejs、Java等語言的客戶端。提供子事務屏障功能,優雅解決冪等、懸掛、空補償等問題。

原文鏈接:https://www.tuicool.com/articles/vQjeQzQ

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 婷婷久久综合 | 三级理论在线观看 | 久久黄视频 | 婷婷日韩| 久久全国免费久久青青小草 | 翁息肉小说老扒 | 2020韩国r级理论片在线观看 | 好湿好紧太硬了我好爽 | 精品久久久久中文字幕日本 | 亚洲色欲色欲综合网站 | 久久热这里面只有精品 | 亚洲美日韩 | 无限资源在线观看8 | 免费α片| 高清女主播一区二区三区 | 色婷婷久久综合中文久久一本 | 青草青视频 | 免费观看一级特黄三大片视频 | 欧美日韩精品一区二区三区视频在线 | 波多野结衣在线看 | 香蕉国产成版人视频在线观看 | 4444kk在线看片| 夫妻性生活在线 | 国内精品91久久久久 | 日本人作爰啪啪全过程 | 国产理论片在线观看 | 波多野结衣女老师 | 深夜www| 变态 调教 视频 国产九色 | 我被男人下药添得好爽 | 色女的乖男人 | 沉沦艳妇杨幂肉体小说 | 国色天香社区在线视频免费观看 | www青青草原| 久久国产精品高清一区二区三区 | 女烈受刑重口小说 | 免费a漫 - 禁密天堂 | 欧美日韩高清观看一区二区 | a级亚洲片精品久久久久久久 | 欧美成人免费tv在线播放 | 亚洲不卡视频在线观看 |