四. Seata 客户端搭建
1. Maven 依赖
com.alibaba.cloud
spring-cloud-starter-alibaba-seata
io.seata
seata-spring-boot-starter
io.seata
seata-spring-boot-starter
1.5.2
2. undo_log 表
undo_log表脚本: https://github.com/seata/seata/blob/1.5.2/script/client/at/db/mysql.sql
-- for AT mode you must to init this sql for you business database. the seata server not need it.
CREATE TABLE IF NOT EXISTS `undo_log`
(
`branch_id` BIGINT NOT NULL COMMENT 'branch transaction id',
`xid` VARCHAR(128) NOT NULL COMMENT 'global transaction id',
`context` VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
`rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info',
`log_status` INT(11) NOT NULL COMMENT '0:normal status,1:defense status',
`log_created` DATETIME(6) NOT NULL COMMENT 'create datetime',
`log_modified` DATETIME(6) NOT NULL COMMENT 'modify datetime',
UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table';
AT模式两阶段提交协议的演变:
一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
二阶段:
提交异步化,非常快速地完成。
回滚通过一阶段的回滚日志进行反向补偿。
Seata的AT模式下之所以在第一阶段直接提交事务,依赖的是需要在每个RM创建一张undo_log表,记录业务执行前后的数据快照。
如果二阶段需要回滚,直接根据undo_log表回滚,如果执行成功,则在第二阶段删除对应的快照数据。
3. 客户端配置
# Seata配置
seata:
enabled: true
# 指定事务分组至集群映射关系,集群名default需要与seata-server注册到Nacos的cluster保持一致
service:
vgroup-mapping:
mall_tx_group: default
# 事务分组配置
tx-service-group: mall_tx_group
registry:
type: nacos
nacos:
application: seata-server
# nacos 服务地址
server-addr: 192.168.10.99:8848
namespace:
group: SEATA_GROUP
以上3点就是 Seata 客户端需要做的事项,下面就 Seata 如何实战应用进行展开详细说明。
五. Seata 实战
Seata 官网示例: http://seata.io/zh-cn/docs/user/quickstart.html
需求
用户购买商品订单支付的业务逻辑。整个业务逻辑由3个微服务提供支持:
商品服务:扣减商品库存。
订单服务:修改订单状态【已支付】。
会员服务:扣减账户余额。
架构图
TM:事务管理器(有来实验室:laboratory)
RM:资源管理器(商城服务:mall-pms;会员服务:mall-ums;订单服务:mall-oms)
TC :事务协调者(Seata服务端:seata-server)
代码实现
有来实验室
实验室在“订单支付”案例中扮演的是【事务管理器】的角色,其工作内容是开始全局事务、提交或回滚全局事务。
按照 【第三节-Seata客户端搭建 】 在 laboratory 模块添加 Maven 依赖和客户端的配置。
订单支付关键代码片段(SeataServiceImpl#payOrderWithGlobalTx),通过注解 GlobalTransactional 开启全局事务,通过对商品 Feign 客户端和订单 Feign 客户端的调用完成订单支付的流程,这是全局事务开始的地方。
/**
* 订单支付(全局事务)
*/
@GlobalTransactional
public boolean payOrderWithGlobalTx(SeataForm seataForm) {
log.info("========扣减商品库存========");
skuFeignClient.deductStock(skuId, 1);
log.info("========订单支付========");
orderFeignClient.payOrder(orderId, ...);
return true;
}
商品服务
按照 【第三节-Seata客户端搭建 】 在 mall-pms 模块添加 Maven 依赖和客户端的配置,在 mall-pms 数据库创建 undo_log 表。
扣减库存关键代码:
/**
* 「实验室」扣减商品库存
*/
public boolean deductStock(Long skuId, Integer num) {
boolean result = this.update(new LambdaUpdateWrapper()
.setSql("stock_num = stock_num - " + num)
.eq(PmsSku::getId, skuId)
);
return result;
}
订单服务
按照 【第三节-Seata客户端搭建 】 在 mall-oms 模块添加 Maven 依赖和客户端的配置,在 mall-oms 数据库创建 undo_log 表。
订单支付关键代码:
/**
* 「实验室」订单支付
*/
public Boolean payOrder(Long orderId, SeataOrderDTO orderDTO) {
Long memberId = orderDTO.getMemberId();
Long amount = orderDTO.getAmount();
// 扣减账户余额
memberFeignClient.deductBalance(memberId, amount);
// 【关键】如果开启异常,全局事务将会回滚
Boolean openEx = orderDTO.getOpenEx();
if (openEx) {
int i = 1 / 0;
}
// 修改订单【已支付】
boolean result = this.update(new LambdaUpdateWrapper()
.eq(OmsOrder::getId, orderId)
.set(OmsOrder::getStatus, OrderStatusEnum.WAIT_SHIPPING.getValue())
);
return result;
}
会员服务
按照 【第三节-Seata客户端搭建 】 在 mall-ums 模块添加 Maven 依赖和客户端的配置,在 mall-ums 数据库创建 undo_log 表。
扣减余额关键代码:
@ApiOperation(value = "「实验室」扣减会员余额")
@PutMapping("/{memberId}/balances/_deduct")
public Result deductBalance(@PathVariable Long memberId, @RequestParam Long amount) {
boolean result = memberService.update(new LambdaUpdateWrapper()
.setSql("balance = balance - " + amount)
.eq(UmsMember::getId, memberId));
return Result.judge(result);
}
测试
以上就基于 youlai-mall 商城订单支付的业务简单实现的 Seata 实验室,接下来通过测试来看看 Seata 分布式事务的能力。
未开启事务
未开启事务前提: 订单状态因为异常修改失败,但这并未影响到商品库存扣减和余额扣减成功的结果,明显这不是希望的结果。
开启事务
开启事务前提:订单状态修改发生异常,同时也回滚了扣减库存、扣减余额的行为,可见 Seata 分布式事务生效。
/
六. Seata 源码
因为 Seata 源码牵涉角色比较多,需要在本地搭建 seata-server 然后和 Seata 客户端交互调试,后面整理出来会单独拿一篇文章进行进行具体分析。
七. 结语
本篇通过 Seata 1.5.2 版本部署到实战讲述了 Seata 分布式事务AT模式在商城订单支付业务场景的应用,相信大家对 Seata 和有来实验室有个初步的认知,但这里还只是一个开始,后续会有更多的热门中间件登上实验室舞台。当然,可见这个舞台很大,所以也希望有兴趣或者有想法同学加入有来实验室的开发。
附. 源码
本文源码已推送至gitee和github仓库
gitee github
后端工程 youlai-mall youlai-mall
前端工程 mall-admin mall-admin