用 Java 轻松完成一个分布式事务 TCC,保姆级教程 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
dongfuye1
V2EX    推广

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

  •  
  •   dongfuye1 2021-09-26 09:58:32 +08:00 1478 次点击
    这是一个创建于 1476 天前的主题,其中的信息可能已经有所发展或是发生改变。

    什么是 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 事务的具体开发

    我们的例子使用的分布式事务框架为 dtm,它对分布式事务的支持非常优雅。下面来详细讲解 TCC 的组成

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

    @RequestMapping("TransOutTry") public Map<String, String> TransOutTry() { Map<String, String> result = new HashMap<>(); result.put("dtm_result", "SUCCESS"); return result; } @RequestMapping("TransOutConfirm") public Map<String, String> TransOutConfirm(HttpServerResponse response) { Map<String, String> result = new HashMap<>(); result.put("dtm_result", "SUCCESS"); return result; } @RequestMapping("TransOutCancel") public Map<String, String> TransOutCancel() { Map<String, String> result = new HashMap<>(); result.put("dtm_result", "SUCCESS"); return result; } @RequestMapping("TransInTry") public Map<String, String> TransInTry() { Map<String, String> result = new HashMap<>(); result.put("dtm_result", "SUCCESS"); return result; } @RequestMapping("TransInConfirm") public Map<String, String> TransInConfirm() { Map<String, String> result = new HashMap<>(); result.put("dtm_result", "SUCCESS"); return result; } @RequestMapping("TransInCancel") public Map<String, String> TransInCancel() { Map<String, String> result = new HashMap<>(); result.put("dtm_result", "SUCCESS"); return result; } 

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

     @RequestMapping("fireTcc") public String fireTcc() { Function<Tcc, Boolean> function = TccController::tccTrans; return tcc.tccGlobalTransaction(function); } public static Boolean tccTrans(Tcc tcc) { try { boolean a = tcc.callBranch("", svc + "/TransOutTry", svc + "/TransOutConfirm", svc + "/TransOutCancel"); boolean b = tcc.callBranch("", svc + "/TransInTry", svc + "/TransInConfirm", svc + "/TransInCancel"); return a && b; } catch (Exception e) { e.printStackTrace(); } return false; } 

    至此,一个完整的 TCC 分布式事务编写完成。

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

    # 部署启动 dtm # 需要 docker 版本 18 以上 git clone https://github.com/yedf/dtm cd dtm docker-compose up # 另起一个命令行 git clone https://github.com/yedf/dtmcli-java-sample.git cd dtmcli-java-sample # 编译运行例子 main/src/main/java/com/github/viticis/dtmclijavaexamples/DtmcliJavaSampleApplication 

    TCC 的回滚

    假如银行将金额准备转入用户 2 时,发现用户 2 的账户异常,返回失败,会怎么样?我们可以让 TransIn 返回失败来模拟这种情况

     @RequestMapping("TransInTry") public Map<String, String> TransInTry() { Map<String, String> result = new HashMap<>(); result.put("dtm_result", "FAILURE"); return result; } 

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

    image.png

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

    在 TCC 事务模式上,有不少的读者会问,如果 Confirm/Cancel 失败会怎么样?这是一个好问题,代表您正在深入思考 TCC 事务模式。第一种情况是临时失败,例如网络故障、应用或数据库宕机,这类错误进行重试,最后会返回成功;另一种情况为业务失败,按照 TCC 的协议,第一阶段锁定资源,保证足够的资源能够让 Confirm/Cancel 执行,也就是说,程序逻辑上,Confirm/Cancel 是不允许返回业务失败的,如果出现业务失败,那么是 bug,需要开发人员手动修复 bug 。

    小结

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

    关于分布式事务更多更全面的知识,请参考《分布式事务最经典的七种解决方案》

    文中使用的例子节选自yedf/dtm,它支持多种事务模式:TCC 、SAGA 、XA 、事务消息 跨语言支持,已支持 golang 、python 、Java 、PHP 、nodejs 等语言的客户端,参考各语言 SDK。提供子事务屏障功能,优雅解决幂等、悬挂、空补偿等问题。

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

    目前尚无回复
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     963 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 23ms UTC 19:01 PVG 03:01 LAX 12:01 JFK 15:01
    o have faith in what you're doing.
    ubao 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