找回密码
 会员注册
查看: 23|回复: 0

DTM在新交易平台的落地

[复制链接]

2万

主题

0

回帖

6万

积分

超级版主

积分
64454
发表于 2024-10-11 20:04:53 | 显示全部楼层 |阅读模式
一、项目背景在项目的发展过程中,我们将整个新交易平台(业务平台部专门为360集团内部打造的类似有赞、微盟的交易系统)微服务化,产生了店铺服务、商品服务、订单服务、优惠券服务、红包服务、用户服务、支付服务、履约服务、售后服务等等。并且这些服务由不同的语言开发。当前端的用户提交订单,服务端需要完成以下操作:创建订单:需要在订单表中创建订单,唯一键为订单ID扣减库存:需要给用户下单的商品扣减库存核销优惠券:用户在下单前,选择了可使用的优惠券,提交订单时,则扣减这部分优惠券扣减红包:用户在下单前,选择了可使用的红包,提交订单时,则扣减这部分红包金额扣减积分:用户在下单前,如果有积分余额,提交订单时,则扣减这部分积分创建支付单:提交订单后,需要创建支付单,最终供用户支付对于上述这个场景,如果在单体订单系统中,很容易使用数据库的事务来解决。但是一旦微服务化了之后,由于这些操作分布在不同的服务中间,则需要按顺序依次去调用各个过程。在这个过程中就会遇到许多进程故障、某一操作无法完成需回滚、重复请求等问题。因此我们就要考虑分布式场景下的事务一致性解决方案。二、分布式方案选型由于订单创建的过程中是需要回滚的,所以首先排除了消息通知类(消息通知、本地事务消息等)的模式。排除之后发现仅剩XA、TCC、SAGA,下面我们分析一下这三种事务模式XAXA协议最初由Tuxedo首先提出,后被提交给X/Open组织作为资源管理器(数据库)与事务管理器的接口标准。XA规范主要定义了全局事务管理器(TM)和局部资源管理器(RM)之间的接口。在XA中,本地的数据库扮演的是RM角色。目前,主流的数据库基本都支持XA事务,包括MySQL、Oracle、SQL Server和PostgreSQL。XA一共分为两阶段:第一阶段(prepare):事务管理器(TM)会向所有参与者发送prepare消息,询问它们是否可以提交事务第二阶段 (commit/rollback):如果所有参与者都能够提交,则事务管理器(TM)向它们发送commit消息,让它们提交事务。如果有任何一个参与者不能提交,则协调器向它们发送rollback消息,让它们回滚事务。通过这两个阶段操作,使所有的参与者数据都能保持一致性,但XA事务模式虽然能够保证分布式事务的一致性,但是由于是数据库层面的资源锁定,会带来一些性能上的开销。排除此模式TCCTCC是Try、Confirm、Cancel三个词语的缩写,最早是由 Pat Helland 于 2007 年发表的一篇名为《Life beyond Distributed Transactions:an Apostate’s Opinion》的论文提出。TCC分为3个阶段Try 阶段:尝试执行,完成所有业务检查(一致性), 预留必须业务资源(准隔离性)Confirm 阶段:如果所有分支的Try都成功了,则走到Confirm阶段。Confirm真正执行业务,不作任何业务检查,只使用 Try 阶段预留的业务资源Cancel 阶段:如果所有分支的Try有一个失败了,则走到Cancel阶段。Cancel释放 Try 阶段预留的业务资源。TCC特点如下:并发度较高,无长期资源锁定开发量较大,需要提供Try/Confirm/Cancel接口一致性较好,不会有暴漏给用户待支付订单,最后又被取消的情况TCC适用于订单类业务,对中间状态有约束的业务但是由于我们系统存在历史逻辑,短时间内无法完全重构以支持资源预留,所以也排除了此模式SAGASAGA最初出现在1987年Hector Garcaa-Molrna & Kenneth Salem发表的论文SAGAS里。其核心思想是将长事务拆分为多个短事务,由Saga事务协调器协调,如果每个短事务都成功提交完成,那么全局事务就正常完成,如果某个步骤失败,则根据相反顺序一次调用补偿操作。Saga分为2个阶段Action阶段:正向执行,无需做资源预留Compensate阶段:如果某一个过程出错,调用补偿接口,依次进行资源逆向补偿Saga事务的特点:并发度高,不用像XA事务那样长期锁定资源需要定义正常操作以及补偿操作,开发量比XA大,但比TCC小一致性较弱,对于订单创建,有可能出现待支付订单,最后又被取消的情况由于Saga仅需要在发生异常需回滚时提供一个逆向补偿的接口,开发量较少,且几乎不用对历史业务逻辑进行改动。故采用此方案而接受短暂数据不一致的问题。三、SAGA模式&DTM在创建订单过程中的实践1、DTM分布式事务管理器的确认确定了Saga模式,接着我们就要进行实施落地,如果自研分布式事务管理器有很大的开发工作,于是去寻找有没有符合我们实际需求的开源工具,最终确定两款比较成熟的开源系统,JAVA SEATA&Golang DTM,由于seata仅支持Dubbo、Spring Cloud等协议,并且除java版本SDK外,其他语言的SDK完善度不高。DTM支持http、grpc协议。最终确定使用DTM。接入DTM后的下单时序如下所示:首先看看,下单 api 的主要处理过程:上面的代码首先创建了一个SAGA事务,然后添加了多个子事务,每个事务分支包括action和compensate两个操作,分别为Add函数的第一第二个参数。子事务定好之后提交给dtm。dtm收到saga提交的全局事务后,会调用所有子事务的正向操作,如果所有正向操作成功完成,那么事务成功结束。如果有正向操作失败,例如账户库存不足,那么dtm会调用各分支的补偿操作,进行回滚,最后事务成功回滚。进程crash问题dtm的saga事务进行过程中,如果发生进程crash,那么dtm会进行重试,保证操作会最终完成回滚问题上述这个saga事务中,如果扣减库存时发现库存不足,则返回failure,会进行回滚。dtm 会记录哪些操作已完成,并回滚相关的操作但是由于分布式事务的引入,因为网络的时序无法保证,会引入幂等、空回滚、悬挂等新问题。2、解决重复请求、空回滚、悬挂等问题分布式事务之所以难,主要是因为分布式系统中的各个节点都可能发生各种非预期的情况。分布式系统最大的敌人可能就是NPC了,在这里它是Network Delay, Process Pause, Clock Drift的首字母缩写。我们先看看具体的NPC问题是什么:Network Delay,网络延迟。虽然网络在多数情况下工作的还可以,虽然TCP保证传输顺序和不会丢失,但它无法消除网络延迟问题。Process Pause,进程暂停。当基于某些需要,例如内存垃圾回收、CPU 排队、服务迁移等,某服务会暂时暂停。Clock Drift,时钟漂移。分布式系统涉及大量的服务器,而不同服务器通常使用 NTP (Network Time Protocol)协议将本地设备的时间与时间服务器对齐对齐后,通常会导致本地时间跳跃。分布式事务既然是分布式的系统,自然也有NPC问题。因为没有涉及时间戳,带来的困扰主要是NP。我们以扣减库存为例,看看NP带来的影响3、网络异常状态下库存扣减问题一般情况下,一个SAGA异步补偿时的执行顺序是,先执行完库存扣减,再执行库存回滚,但是由于N,则有可能库存扣减的网络延迟大,导致先执行库存回滚,再执行库存扣减。这种情况就引入了分布式事务中的两个难题:空补偿:compensate执行时,action未执行,事务分支的compensate操作需要判断出action未执行,这时需要忽略compensate中的业务数据更新,直接返回悬挂:action执行时,compensate已执行完成,事务分支的action操作需要判断出compensate已执行,这时需要忽略action中的业务数据更新,直接返回分布式事务还有一类需要处理的常见问题,就是重复请求幂等:由于任何一个请求都可能出现网络异常,出现重复请求,所有的分布式事务分支操作,都需要保证幂等性上图中,库存扣减操作超时而执行的库存回滚,若在商品服务执行成功,但反馈的结果由于 NPC 问题不能到达事务调度器,那么事务调度器还有可能再次发送库存回滚。这就意味着商品服务的库存回滚操作会被多次重复调用。我们必须保证分布式事务的全部操作分支保证幂等性。也就是重复调用操作分支,但不会产生叠加的影响。不论空补偿、悬挂还是幂等,都需要在业务逻辑层面做出判定。通常的做法是通过分布式事务事件日志的方案来标识操作状态,进而决定是否需要处理空补偿和防止悬挂。4、DTM解决方案在库存扣减服务中的实现如果没有一个通用的操作方法去解决幂等、悬挂、空回滚等问题,系统也面临着大量的开发工作,并且每个业务都要仔细处理这三张问题。好在看到了DTM的子事务屏障技术。子事务屏障技术的原理是,在本地数据库,建立分支操作状态表dtm_barrier,唯一键为全局事务id-分支id-分支操作开启本地事务对于当前操作op(action|compensate),insert ignore一条数据gid-branchid-op,如果插入不成功,提交事务返回成功(常见的幂等控制方法)如果当前操作是compensate,那么在insert ignore一条数据gid-branchid-action,如果插入成功(注意是成功),则提交事务返回成功调用屏障内的业务逻辑,如果业务返回成功,则提交事务返回成功;如果业务返回失败,则回滚事务返回失败由于我们的库存服务使用的是java语言,DTM中java版本SDK还不支持SAGA事务的子事务屏障,于是我们使用此原理自己研发在此机制下,我们看一下怎么解决乱序相关的问题空补偿控制:如果action没有执行,直接执行了compensate,那么插入gid-branchid-action会成功,不走屏障内的逻辑,保证了空补偿控制幂等控制:gid-branchid-{action/compensate}在任何一个操作都无法重复插入唯一键,保证了不会重复执行防悬挂控制:action在compensate之后执行,由于执行compensate时会在插入gid-branchid-action,导致action请求插入gid-branchid-action不成功,就不执行屏障内的逻辑,保证了防悬挂控制总结伴随着业务的快速地发展、越来越高的业务复杂度,几乎每个公司的系统都会从单体走向分布式,特别是转向微服务架构。分布式事务本身就是一个技术难题,如果没有合适的框架、工具,分布式事务会大大的提高流程的复杂度,会带来很多额外的开销工作。经过我们调研和探索,很好地利用DTM事务管理器与系统结合。将分布式事务相关逻辑全部交由 DTM处理,而让我们相应的开发者更聚焦于业务本身,只需要安心写好相关操作和补偿操作即可。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2024-12-27 15:09 , Processed in 0.388950 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表