使用Seata处理分布式事务

使用Seata处理分布式事务,第1张

使用Seata处理分布式事务

文章目录
  • 使用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处理分布式事务的例子。但该种方案不适合高并发的场景,比较适合处理并发量低的场景。高并发下,还是建议使用柔性事务解决方案,允许暂时的不一致性,保证最终一致性即可。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/741737.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-28
下一篇 2022-04-28

发表评论

登录后才能评论

评论列表(0条)

保存