全局事务管理最佳实践
本实践主要介绍通过全局事务服务解决分布式系统中的数据一致性问题。
前提条件
示例基于阿里云专有云3.14及以上版本,最佳实践主要涉及阿里云全局事务管理服务GTS,由阿里云专有云消息队列MQ、云数据库RDS支持。
背景信息
全局事务服务GTS(Global Transaction Service)是一款高性能、高可靠、接入简单的分布式事务中间件,用于解决分布式环境下的数据一致性问题。
一个完整的业务往往需要调用多个子业务或服务,随着业务的不断增多,涉及的服务及数据也越来越多,越来越复杂。传统的系统难以支撑,出现了应用和数据库等的分布式系统。分布式系统又带来了数据一致性的问题,从而产生了分布式事务。
分布式事务是指事务发起者、资源管理器、事务协调者及资源分别位于不同的分布式系统的不同节点之上。
在单机数据库下很容易维持事务的ACID(Atomicity、Consistency、Isolation、Durability)特性,但在分布式系统中并不容易,GTS可以保证分布式系统中的分布式事务的ACID特性。
GTS支持DRDS、RDS、MySQL等多种数据源,可以配合企业级分布式应用服务EDAS和Dubbo等微服务框架使用, 兼容MQ实现事务消息。通过各种组合,可以轻松实现分布式数据库事务、多库事务、消息事务、服务链路级事务等多种业务需求。
应用场景
GTS可应用在涉及数据库操作的多个领域,包括但不限于金融支付、电信、电子商务、快递物流、广告营销、社交、即时通信、手游、视频、物联网、车联网等。
解决使用DRDS分库分表后产生的跨分库事务问题。
DRDS通过分库分表实现数据水平拆分, 来解决单机关系型数据库扩展性问题。但是原有单库单表进行分库分表后, 单表的数据被分散到多个库的表中, 原来对单表多行数据进行的变更, 可能会变为对多库多表的数据变更,即单机本地事务变成了分布式事务。
DRDS本身不支持分布式事务, 上述场景下再采用原来的单库事务进行操作会导致失败。在DRDS中加入GTS能够实现这种多个库交易操作的原子性,解决分布式数据库跨库事务的问题。
跨分库事务场景
解决跨服务的事务问题。
现代IT应用中,服务化的模式得到了广泛使用。比如大型电商应用中,为了系统解耦,常常将整个应用划分为多个系统,如商品系统、商家系统、用户系统、账务系统、物流系统等,各个系统会提供各自的服务。
一个简单的商品加入购物车的操作,会调用商品系统的服务来减掉库存,调用购物车系统的服务增加记录,调用结算系统的服务变更待结算金额等等操作。
使用GTS可以将调用这些服务的操作加入到一个全局事务中,让他们要么同时成功,要么同时失败,保证了各个系统的数据一致性。
跨服务事务场景
GTS配置MQ可以快速解决事务消息问题。
有些系统在使用数据库保证系统内数据一致的同时, 也会使用消息队列(MQ)作为和其他系统间的消息传递, 完成不同系统间的数据一致。
一个典型的场景,A系统成功将本地数据1保存到数据库后, 通过MQ向B系统发送一条通知消息,B系统收到消息后保存与数据1关联的数据2,AB两个系统保持数据一致。但是当A系统成功保存数据但是未能成功调用消息系统发送通知时, 会导致A系统中有数据1而B系统中没有相应的数据2, 即A、B两个系统出现数据不一致,造成系统故障。
对于上述类似场景,能够将A系统向数据库写入数据1的本地事务, 和通过MQ向B系统发送通知放到一个全局事务中, 保证数据写入则消息一定发出,数据未写入则消息一定不会发出。
事务消息场景
实践步骤
准备阶段。
登录Apsara Uni-manager运维控制台,在右上方导航栏单击运维>产品运维管理>GTS,登录GTS控制台,获取账号下的事务分组。
单击需要操作的事务分组名称,可查看事务总览。
单击查看事务,查看事务实例监控。
基于跨服务、数据库分布式事务的实践。
样例简介。
该样例模拟了一个用户订购商品的业务系统,包含3个微服务:
Storage:库存服务,扣减指定商品的库存数量。
Order:订单服务,根据采购请求生成订单。
Account:账户服务,从用户账户中扣减金额。
样例逻辑。
在初始模块Business开启全局事务,分别调用Storage库存服务和Order订单服务;
库存服务扣除库存并更新库存数据库;
订单服务创建订单,更新订单数据库,并调用Account账户服务;
账户服务扣除余额并更新账户的数据库;
为了展示GTS全局事务服务,我们没有在每一步进行判断,比如库存、账户余额是否为零,而是在最后进行判断,如果判断成功,即样例执行第一次,事务就是成功的;
而样例的第二次执行,会将账户扣减为负数,最后的判断失败,样例事务会全部回滚,即第二次执行无效,库存、订单、账户均回滚至第二次执行之前;
下载样例工程。
代码逻辑。
在application配置文件中,对事务分组和接入点及数据库进行了定义。
在BusinessController中,开启事务方法。
对购买这个入口方法,进行全局事务的注解,打印全局事务ID,并调用库存扣减接口和订单创建接口,最后进行账户或库存的判断;方式即对账户余额是否为负进行判断;
库存服务中,首先是在StorageController中,开启扣减库存的方法;
在扣减库存的方法中,打印全局事务ID,并调用数据库接口,进行库存的更新;
在订单服务,OrderController中,开始生产订单的方法,打印全局事务ID,对订单总价进行计算,生成订单,并将订单信息更新至数据库中;并调用账户服务接口;
最后是账户服务,打印全局事务ID,更新账户金额;
服务部署。
登录Apsara Uni-manager运营控制台,在上方导航栏单击产品>中间件>企业级分布式应用服务 EDAS,进入EDAS服务控制台。
单击应用管理>应用列表>创建应用,选择对应的集群信息、运行环境。
按照图示信息将编辑完成Demo的jar包,上传到EDAS平台,完成应用部署。
部署完成的四个服务示例:
运行验证。
访问路口方法:http://服务实际部署IP:8080/purchase。
第一次运行:事务成功,打印日志。
在EDAS控制台单击bussiness进入应用详情,单击实时日志,查看日志,跨服务调用展示。
数据库更新完成。
订单数据库添加完成。库存数据库扣减完成。账户数据库扣减完成。
第二次运行:访问失败。如样例逻辑中展示,将账户扣减为负数,最后的判断失败,样例事务会全部回滚。
在企业级分布式应用服务EDAS控制台单击bussiness进入应用详情,单击实时日志,查看日志,打印部分日志,未完成事务。
数据库回滚。
订单数据库回滚,无添加。库存数据库回滚,无扣减。账户数据库回滚,无扣减。验证完成。
基于跨数据库、消息队列分布式事务的实践。
样例简介。
样例工程模拟了资金转账的应用。包含2个数据源,和1个MQ事务消息Topic。
帐户A数据源:存储A的资金。
发送账户转账成功消息。
帐户B数据源:存储B的资金。
样例逻辑。
跨数据库的消息队列分布式事务,包含两个数据源和一个MQ事务消息Topic;进行资金转账的模拟,初始资金均为1W,从账户A每次转2000,并发出MQ消息,账户B存入2000;最后进行判断账户A的余额;如果余额为负,则回滚前面的A扣款,MQ消息发送,B存款等操作;
下载样例工程。
代码逻辑。
在配置文件中,对事务分组和接入点、数据库及MQ进行了定义;
在Application中,初始化账户A、B,并对账户信息进行日志打印,做了十次转账的循环,在循环中,调用转账的方法;
对转账这个入口方法进行全局事务注解,先从数据源A的帐户扣款,然后发MQ消息,接着从数据源B的帐户存款;日志输出现有的数据库信息,即A、B的余额,最后判断A的余额,如果小于零则抛出异常,全局事务服务GTS会回滚前面的操作;
服务部署。
登录Apsara Uni-manager运营控制台,在上方导航栏单击产品>中间件>企业级分布式应用服务 EDAS,进入EDAS服务控制台;
在左侧导航栏单击应用管理>应用列表>创建应用,选择对应的集群信息、运行环境。
将编辑完成Demo的jar包,上传到企业级分布式应用服务EDAS平台,完成应用部署。
部署完成的服务示例。
运行验证。
运行应用的转账方法,打印日志。
在企业级分布式应用服务EDAS控制台单击biz进入应用详情,单击实时日志,查看日志。
前五次转账成功。第六次至第十次转账失败,数据库及MQ回滚。
MQ验证。
查询第五次发送的消息IDAC1901E0000106DC148446D9CB050010,可看到Topic信息。查询第六次发送的消息IDAC1901E0000106DC148446E331AC0016,消息回滚,无结果。A/B数据库转账及回滚完成。验证完成。
- 本页导读