当前位置: 首页 > 后端技术 > PHP

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

时间:2023-03-29 19:24:55 PHP

什么是TCC,TCC是Try、Confirm、Cancel这几个词的缩写,最早由PatHelland于2007年发表的论文介绍。TCC组成TCC分为3个阶段Try阶段:尝试执行,完成所有业务检查(一致性),预留必要的业务资源(准隔离)Confirm阶段:如果Try的所有分支都成功,则进入Confirm阶段。Confirm实际执行业务,不做任何业务检查,只使用Try阶段预留的业务资源。Cancel阶段:如果所有分支中有一个Try失败,则进入Cancel阶段。Cancel释放Try阶段预留的业务资源。在TCC分布式事务中,有3个角色,与经典的XA分布式事务相同:AP/应用程序,发起全局事务,定义全局事务中包含哪些事务分支RM/资源管理器,负责分支交易各种资源的管理TM/transactionmanager负责协调全局交易的正确执行,包括Confirm和Cancel的执行,以及处理网络异常。如果我们要进行一个类似于银行跨行转账的业务,分别在不同的微服务中转出(TransOut)和转入(TransIn),一个成功完成TCC交易的典型时序图如下:TCC实践接下来,我们将制定具体的TCC交易。TCC目前可用的开源框架主要是Java语言,以seata为代表。我们的例子使用的是nodejs,使用的分布式事务框架是dtm,非常优雅的支持分布式事务。下面详细介绍TCC的组合下面我们来编写实体的Try/Confirm/Cancel的处理函数$vega->handleFunc('/api/TransOutTry',function(Mix\Vega\Context$ctx){var_dump('TransOutTry',$ctx->request->getQueryParams(),$ctx->request->getParsedBody());$ctx->JSON(200,['result'=>'SUCCESS']);})->methods('POST');$vega->handleFunc('/api/TransOutConfirm',function(Mix\Vega\Context$ctx){var_dump('TransOutConfirm',$ctx->request->getQueryParams(),$ctx->请求->getParsedBody());$ctx->JSON(200,['result'=>'SUCCESS']);})->methods('POST');$vega->handleFunc('/api/TransOutCancel',函数(Mix\Vega\Context$ctx){var_dump('TransOutCancel',$ctx->request->getQueryParams(),$ctx->request->getParsedBody());$ctx->JSON(200,['result'=>'SUCCESS']);})->methods('POST');$vega->handleFunc('/api/TransInTry',function(Mix\Vega\Context$ctx){var_dump('TransInTry',$ctx->请求->getQueryParams(),$ctx->请求->getParsedBody());$ctx->JSON(200,['result'=>'SUCCESS']);})->methods('POST');$vega->handleFunc('/api/TransInConfirm',函数(Mix\Vega\上下文$ctx){var_dump('TransInConfirm',$ctx->request->getQueryParams(),$ctx->request->getParsedBody());$ctx->JSON(200,['result'=>'SUCCESS']);})->methods('POST');$vega->handleFunc('/api/TransInCancel',function(Mix\Vega\Context$ctx){var_dump('TransInCancel',$ctx->request->getQueryParams(),$ctx->request->getParsedBody());$ctx->JSON(200,['result'=>'SUCCESS']);})->methods('POST');至此,各个子交易的处理函数都OK了,接下来开始TCC交易,进行分支调用Dtmcli\tccGlobalTransaction($dtm,function($tcc)use($svc){/**@varDtmcli\Tcc$tcc*/$req=['amount'=>30];$tcc->callBranch($req,$svc.'/TransOutTry',$svc.'/TransOutConfirm',$svc.'/TransOutCancel');$tcc->callBranch($req,$svc.'/TransInTry',$svc。'/TransInConfirm',$svc.'/TransInCancel');});至此,一个完整的TCC分布式事务就写好了。如果想运行成功的例子,请参考这个例子yedf/dtmcli-php-sample,运行起来很简单#部署并启动dtm#需要docker版本18以上gitclonehttps://github.com/yedf/dtmcddtmdocker-composeup#再创建一个命令行https://github.com/yedf/dtmcli-php-sample.gitcddtmcli-php-samplecomposerinstallphpdemo.phpstartTCCrollback如果银行转账给用户2,发现用户2的账号异常,返回失败,会发生什么??我们可以通过让TransIn返回失败来模拟这种情况$vega->handleFunc('/api/TransInTry',function(Mix\Vega\Context$ctx){var_dump('TransInTry',$ctx->request->getQueryParams(),$ctx->request->getParsedBody());$ctx->JSON(200,['result'=>'FAILURE']);})->methods('POST');我们给出事务失败的交互时序图和成功的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项目,给个star支持!