文章目录
- 使用Seata处理分布式事务
- 前言
- 一、Seata架构
- 二、效果展示
- 三、代码实现
- 总结
前言
电商项目中经常会遇到需要处理分布式事务场景。比如用户买东西,会进行下订单,扣减库存,扣用户账号余额等系列 *** 作,在单服务器下,我们可以利用本地事务保证这一系列 *** 作的原子性,然而,在分布式的情况下,事务就失效了。Seata就是处理这类问题的开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。使用到的技术有:Seata,Dubbo,mysql
一、Seata架构
以下是官网提供的Seata架构图
TC (Transaction Coordinator) - 事务协调者
维护全局和分支事务的状态,驱动全局事务提交或回滚。
TM (Transaction Manager) - 事务管理器
定义全局事务的范围:开始全局事务、提交或回滚全局事务。
RM (Resource Manager) - 资源管理器
管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
AT模式:
一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
二阶段:提交异步化,非常快速地完成。回滚通过一阶段的回滚日志进行反向补偿。
这里使用Seata提供的AT事务模式。场景说明:用户购买商品,调用库存服务扣减库存和订单服务生成订单,订单服务调用账户服务扣余额,要保证这一系列 *** 作的原子性,要么全成功,要么全失败。
本例子中在订单生成完毕,以及账户扣减余额之后,加入10/0这样的有问题的代码,看已执行成功的 *** 作是否会全部回滚
Begin new global transaction表明开始了分布式事务
Branch Rollbacked result: PhaseTwo_Rollbacked表示第二阶段出现异常,回滚成功。
再抛出异常前让程序等待5s,就可以看到回滚日志了。回滚日志如下:
查看数据库,发现数据没发生任何变化,回滚成功,分布式事务生效
购买商品,在事务入口处添加 @GlobalTransactional注解开启全局事务
/**
* 购买商品
*/
@GlobalTransactional
public void purchase(String userId, String commodityCode, int orderCount) throws InterruptedException {
//库存服务减库存
storageService.deduct(commodityCode, orderCount);
//生成新订单
orderService.create(userId, commodityCode, orderCount);
}
库存服务,扣减库存
@Transactional
public void deduct(String commodityCode, int count) {
storageMapper.deduct(commodityCode,count);
}
生成订单
@Transactional
public int create(String userId, String commodityCode, int orderCount) {
int orderMoney = calculate(commodityCode, orderCount);
accountService.debit(userId, orderMoney);
OrderTbl order = new OrderTbl();
order.setUserId(userId);
order.setCommodityCode(commodityCode);
order.setCount(orderCount);
order.setMoney(orderMoney);
orderMapper2.insertSelective(order);
// 解开下面注释,测试Seata分布式事务回滚
// boolean flag=false;
// if (!flag) {
// throw new RuntimeException("测试抛异常后,分布式事务回滚!");
// }
// INSERT INTO orders ...
return 1;
}
private int calculate(String commodityCode, int orderCount) {
//TODO 拿到该商品的价格,乘以该商品的数量,假设该商品的价格为10
return 10*orderCount;
}
账户服务,扣减余额
@Transactional
public void debit(String userId, int money) {
accountMapper.debit(userId,money);
}
其中,微服务之间的远程调用使用Dubbo来实现
使用@DubboService来暴露服务
@DubboService
@Service
public class AccountServiceImpl implements AccountService {
//账号服务
}
使用@DubboReference来引入服务
@DubboReference
AccountService accountService;
总结
例如:以上就是使用Seata处理分布式事务的例子。但该种方案不适合高并发的场景,比较适合处理并发量低的场景。高并发下,还是建议使用柔性事务解决方案,允许暂时的不一致性,保证最终一致性即可。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)