本文将介绍Seata的引入方式和使用方式。环境基于雨润后端的开发环境(已配置好seata的Common包)。用法讲解基于现有的电商(yurun-service-mall的seata分支)项目进行概述。

1.电商项目概览

雨润的电商项目采用的是一种微服务架构方式。其具体的架构图如下:
image.png
如图所指示,mall-app-sale和mall-app-operation调用底层的交易、商品、用户中心等服务。用户中心使用的是mall-user数据库,其他底层服务均使用mall-trade数据库。

2.Seata的引入方式

  • 整个工程的父pom文件引入 ```xml
    1. <dependencies>
  1. <dependency>
  2. <groupId>com.yurun</groupId>
  3. <artifactId>yurun-common-seata</artifactId>
  4. <version>${yurun.common.version}</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>io.seata</groupId>
  8. <artifactId>seata-spring-boot-starter</artifactId>
  9. <version>1.4.1</version>
  10. </dependency>
  11. </dependencies>
  12. </dependencyManagement>
  1. - 涉及到分布式事务的服务(调用方或被调用方都要引入seata包)
  2. ```xml
  3. <dependencies>
  4. <!--seata分布式事务-->
  5. <dependency>
  6. <groupId>com.yurun</groupId>
  7. <artifactId>yurun-common-seata</artifactId>
  8. </dependency>
  9. </dependencies>

在电商中,架构图中的所有服务的pom文件都引入了此项依赖

3.使用方式

接下来我会介绍几种常见的使用方式,大家可以根据具体的情况来选择。

3.1常规Service层使用

常规Service层的使用方式,和Spring的本地事务使用方式一样,在Service层上加上@GlobalTransaction注解。(注:如果原来的方法有@Transactional注解,也无所谓可以都加在方法上不影响)
code-snapshot.png

3.2方法已经try-catch的回滚方法

大部分项目中,不存在这样的情况。但是电商中就有这样的情况,一个Controller层方法被try-catch,里面调用各个底层服务。这种接口在 mall-app-sale和mall-operation中基本上都是。其解决案例可以看一下具体的几个接口:(/MALL140102 , /MALL150102),其关键点是通过Seata提供api方式回滚,下面代码模拟大致解决思路
code-snapshot.png
手动回滚全局事务的代码:

  1. GlobalTransactionContext.reload(RootContext.getXID()).rollback();

3.3一个方法中的某些数据库操作不走全局事务

有些场景下你希望同一个方法中的某些操作不进入全局事务管控,失败就失败,不管他,比如,我们的内购的要求是订单生成成功,库存可以扣减不成功。具体的案例可以看电商接口:(MALLAPP180101),其主要解决思路是,解绑xid。
code-snapshot.png
关键代码

  1. public void testDemo(){
  2. //............
  3. 需要全局事务的代码
  4. //............
  5. String unbindXid = RootContext.unbind();
  6. try {
  7. //不用全局事务的代码块
  8. } catch (Exception e) {
  9. log.error("内购订单生成-记录活动明细和扣减库存部分异常", e);
  10. } finally {
  11. if (unbindXid != null) {
  12. RootContext.bind(unbindXid);
  13. }
  14. }
  15. //......
  16. 需要全局事务的代码
  17. //.....
  18. }

3.5服务中包含队列,ES等AT模式无法控制的操作时

这种情况下,个人选择将SQL相关操作和队列或ES的操作所使用的Seata模式分开,SQL相关操作还是用AT模式来控制,而队列、ES的操作使用TCC模式。具体例子可以看电商的(createNewOrder 和 delShopItem)一个是创建订单需要发送延时消息到队列,一个是删除商品,需要同时删除ES中的商品。TCC具体如何去编写。请看TCC如何使用的文章。TCC总结下来就是3步
①定义@LocalTCC接口
②实现try 、commit和rollback接口
③在Service层引用即可