用 Python 轻松完成一个分布式事务 TCC,保姆级教程 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
如果想在 V2EX 获得更好的推广效果,欢迎了解 PRO 会员机制:
pro/about
dongfuye1

用 Python 轻松完成一个分布式事务 TCC,保姆级教程

  •  
  •   dongfuye1 Aug 6, 2021 1773 views
    This topic created in 1724 days ago, the information mentioned may be changed or developed.

    什么是分布式事务?银行跨行转账业务是一个典型分布式事务场景,假设 A 需要跨行转账给 B,那么就涉及两个银行的数据,无法通过一个数据库的本地事务保证转账的 ACID,只能够通过分布式事务来解决。

    分布式事务就是指事务的发起者、资源及资源管理器和事务协调者分别位于分布式系统的不同节点之上。在上述转账的业务中,用户 A-100 操作和用户 B+100 操作不是位于同一个节点上。本质上来说,分布式事务就是为了保证在分布式场景下,数据操作的正确执行。

    什么是 TCC 分布式事务,TCC 是 Try 、Confirm 、Cancel 三个词语的缩写,最早是由 Pat Helland 于 2007 年发表的一篇名为《 Life beyond Distributed Transactions:an Apostate’s Opinion 》的论文提出。

    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 事务典型的时序图如下:

    image.png

    TCC 实践

    下面我们进行一个 TCC 事务的具体开发

    目前可用于 TCC 的开源框架,主要为 Java 语言,其中以 seata 为代表。我们的例子采用 python,使用的分布式事务框架为dtm,它对分布式事务的支持非常优雅。下面来详细讲解 TCC 的组成

    下面我们来编写具体的 Try/Confirm/Cancel 的处理函数

    @app.post("/api/TransOutTry") def trans_out_try(): return {"dtm_result": "SUCCESS"} @app.post("/api/TransOutConfirm") def trans_out_confirm(): return {"dtm_result": "SUCCESS"} @app.post("/api/TransOutCancel") def trans_out_cancel(): return {"dtm_result": "SUCCESS"} @app.post("/api/TransInTry") def trans_in_try(): return {"dtm_result": "SUCCESS"} @app.post("/api/TransInConfirm") def trans_in_confirm(): return {"dtm_result": "SUCCESS"} @app.post("/api/TransInCancel") def trans_in_cancel(): return {"dtm_result": "SUCCESS"} 

    到此各个子事务的处理函数已经 OK 了,然后是开启 TCC 事务,进行分支调用

    # 这是 dtm 服务地址 dtm = "http://localhost:8080/api/dtmsvr" # 这是业务微服务地址 svc = "http://localhost:5000/api" @app.get("/api/fireTcc") def fire_tcc(): # 发起 tcc 事务,其中 tcc_trans 进行具体的业务处理 gid = tcc.tcc_global_transaction(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 分布式事务编写完成。

    如果您想要完整运行一个成功的示例,那么参考这个例子yedf/dtmcli-py-sample,将它运行起来非常简单

    # 部署启动 dtm # 需要 docker 版本 18 以上 git clone https://github.com/yedf/dtm cd dtm docker-compose up # 另起一个命令行 git clone https://github.com/yedf/dtmcli-py-sample cd dtmcli-py-sample pip3 intall flask dtmcli requests flask run # 另起一个命令行 curl localhost:5000/api/fireTcc 

    TCC 的回滚

    假如银行将金额准备转入用户 2 时,发现用户 2 的账户异常,返回失败,会怎么样?

    我们将前面的 TransIn 处理函数,改成返回失败,整个事务最后就会失败回滚

    @app.post("/api/TransInTry") def trans_in_try(): return {"dtm_result": "FAILURE"} 

    我们给出事务失败交互的时序图

    image.png 这个跟成功的 TCC 差别就在于,当某个子事务返回失败后,后续就回滚全局事务,调用各个子事务的 Cancel 操作,保证全局事务全部回滚。

    TCC 网络异常

    TCC 在整个全局事务的过程中,可能发生各类网络异常情况,典型的是空回滚、幂等、悬挂,由于 TCC 的异常情况,和 SAGA 、可靠消息等事务模式有相近的地方,因此我们把所有异常的解决方案统统放在这篇文章《子事务屏障,一个函数调用搞定分布式事务乱序》进行讲解

    小结

    在这篇文章里,我们介绍了 TCC 的理论知识,也通过一个例子,完整给出了编写一个 TCC 事务的过程,涵盖了正常成功完成,以及成功回滚的情况。相信读者通过这边文章,对 TCC 已经有了深入的理解。

    阅读完此篇干货,欢迎大家访问https://github.com/yedf/dtm项目,给颗星星支持!

    dtm支持多种语言,包括 GO 、python 、php 、node,您可以点击各语言客户端及示例访问

    No Comments Yet
    About     Help     Advertise     Blog     API     FAQ     Solana     5607 Online   Highest 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 48ms UTC 08:33 PVG 16:33 LAX 01:33 JFK 04:33
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86