Springcloud seata分布式事务实现代码解析

作者:梦泽千秋 时间:2022-12-27 20:14:01 

Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。本篇不涉及其原理,只用代码构建项目简单试用一下其回滚的机制。

大致上seata分为TC,TM,RM三大构建成整体。它们之间的包含关系如下。即一(xid主键编码,记录信息)带三(TC,TM,RM)

Springcloud seata分布式事务实现代码解析

下面之间构建项目进行测试。

1.下载seata并解压,然后改动配置文件。

http://seata.io/zh-cn/blog/download.html官网下载。

解压之后到conf中修改file和registry文件,修改之前一定记得先备份。

file.conf,改动两个地方

将group后面的参数定义一个名字,随意

Springcloud seata分布式事务实现代码解析

存储方式选db放在数据库,自然其配置信息根据自己的数据库去填写。

Springcloud seata分布式事务实现代码解析

然后是register文件,填写信息将seata注册到nacos中。

Springcloud seata分布式事务实现代码解析

启动自然是在bin中打开bat文件即可,注意需要先启动naco。

2.构建项目(order,storage,account)

演示整体的服务调用还有服务报错的时候进入回滚。通过创建订单->检查库存并扣除->检查账户并扣除->修改订单状态

具体代码可查看GitHub

https://github.com/MaTsukun/springcloud2020

关键的service方法


@Service
@Slf4j
public class OrderServiceImpl implements OrderService{
 @Resource
 private OrderMapper orderMapper;
 @Resource
 private StorageService storageService;
 @Resource
 private AccountService accountService;

@Override
 @GlobalTransactional(name="abc-create-order",rollbackFor = Exception.class)
 public void create(Order order){
   //1.创建订单
   log.info("开始创建订单");
   orderMapper.create(order);
   //2.减少库存
   log.info("查询库存并且进行更改");
   storageService.decrease(order.getProductId(),order.getCount());
   //3.扣除费用
   log.info("查询余额并扣除费用");
   accountService.updateAccount(order.getUserId(),order.getMoney());
   //4.修改状态
   log.info("更改订单状态");
   orderMapper.update(order.getUserId(),0);
   log.info("订单结束,O(∩_∩)O哈哈~");
 }
}

可以看到在order项目中同时调用了storage和account的项目的方法,采用的是openfeign,整体形成了一个链路,成为一个整的事务。

而添加的GlobalTransactional注解则保证了事务中任何一方出现错误就会使整个项目的执行过程进行回滚,而不是单事务的回滚。

3.seata回滚原理

在每次注解的方法里进行执行sql语句的时候都会创建一个id记录此次的写操作同时在每次的写操作前后都会生成前置记录和后置记录,可以在出现错误回滚的时候,通过记录进行逆操作回滚重新将数据写回去。

通过数据库配置的seata库展示可以看见对应的记录id信息,通过debug模式暂停服务,查看记录的信息。

global的全局xid

Springcloud seata分布式事务实现代码解析

account表的undo记录

Springcloud seata分布式事务实现代码解析

记录的信息json格式


{
 "@class": "io.seata.rm.datasource.undo.BranchUndoLog",
 "xid": "192.168.2.141:8091:2060193863",
 "branchId": 2060193875,
 "sqlUndoLogs": [
   "java.util.ArrayList",
   [
     {
       "@class": "io.seata.rm.datasource.undo.SQLUndoLog",
       "sqlType": "UPDATE",
       "tableName": "t_account",
       "beforeImage": {
         "@class": "io.seata.rm.datasource.sql.struct.TableRecords",
         "tableName": "t_account",
         "rows": [
           "java.util.ArrayList",
           [
             {
               "@class": "io.seata.rm.datasource.sql.struct.Row",
               "fields": [
                 "java.util.ArrayList",
                 [
                   {
                     "@class": "io.seata.rm.datasource.sql.struct.Field",
                     "name": "id",
                     "keyType": "PrimaryKey",
                     "type": -5,
                     "value": [
                       "java.lang.Long",
                       1
                     ]
                   },
                   {
                     "@class": "io.seata.rm.datasource.sql.struct.Field",
                     "name": "used",
                     "keyType": "NULL",
                     "type": 3,
                     "value": [
                       "java.math.BigDecimal",
                       600
                     ]
                   },
                   {
                     "@class": "io.seata.rm.datasource.sql.struct.Field",
                     "name": "residue",
                     "keyType": "NULL",
                     "type": 3,
                     "value": [
                       "java.math.BigDecimal",
                       400
                     ]
                   }
                 ]
               ]
             }
           ]
         ]
       },
       "afterImage": {
         "@class": "io.seata.rm.datasource.sql.struct.TableRecords",
         "tableName": "t_account",
         "rows": [
           "java.util.ArrayList",
           [
             {
               "@class": "io.seata.rm.datasource.sql.struct.Row",
               "fields": [
                 "java.util.ArrayList",
                 [
                   {
                     "@class": "io.seata.rm.datasource.sql.struct.Field",
                     "name": "id",
                     "keyType": "PrimaryKey",
                     "type": -5,
                     "value": [
                       "java.lang.Long",
                       1
                     ]
                   },
                   {
                     "@class": "io.seata.rm.datasource.sql.struct.Field",
                     "name": "used",
                     "keyType": "NULL",
                     "type": 3,
                     "value": [
                       "java.math.BigDecimal",
                       700
                     ]
                   },
                   {
                     "@class": "io.seata.rm.datasource.sql.struct.Field",
                     "name": "residue",
                     "keyType": "NULL",
                     "type": 3,
                     "value": [
                       "java.math.BigDecimal",
                       300
                     ]
                   }
                 ]
               ]
             }
           ]
         ]
       }
     }
   ]
 ]
}

可以看到里面有beforeimage和afterimage快照记录,通过这些记录可以实现逆操作,重新写进数据实现回滚。

本文只是简单的配置,后续会进行详细补充。

所有的代码都在GitHub

https://github.com/MaTsukun/springcloud2020

来源:https://www.cnblogs.com/lin530/p/14048973.html

标签:Spring,cloud,seata,分布,事务
0
投稿

猜你喜欢

  • java Disruptor构建高性能内存队列使用详解

    2022-02-03 00:41:14
  • Java Netty实现心跳机制过程解析

    2023-05-24 21:27:10
  • 理解Android中Activity的方法回调

    2023-06-27 13:35:36
  • 如何在C#项目中链接一个文件夹下的所有文件详解

    2022-09-08 08:48:22
  • 基于C语言的库封装发布技术详解

    2023-10-14 05:58:46
  • 详解Jackson 使用以及性能介绍

    2023-02-21 00:08:31
  • JAVA设计模式之建造者模式原理与用法详解

    2022-09-25 19:43:04
  • java实现图片写入高清字体及带边框的方法

    2023-11-29 03:43:47
  • 详解三种C#实现数组反转方式

    2023-10-09 20:29:23
  • Android仿QQ6.0主页面侧滑效果

    2022-08-06 03:44:16
  • C#使用webbrowser的常见用法实例

    2023-06-14 13:57:45
  • golang实现java uuid的序列化方法

    2022-12-30 11:30:57
  • Feign调用可重试的最佳方案分享

    2021-06-09 12:19:23
  • java导出csv格式文件的方法

    2022-07-24 20:26:36
  • 详解SpringBoot的事务管理

    2022-01-15 13:39:26
  • Flutter快速制作一个水印组件实例详解

    2023-05-16 23:32:20
  • Mybatis的介绍、基本使用、高级使用

    2023-03-26 02:47:18
  • C语言 简单粗暴的笨方法找水仙花数

    2023-11-27 21:53:29
  • 深入理解MyBatis中的一级缓存与二级缓存

    2022-05-25 09:41:34
  • java使用ftp上传文件示例分享

    2021-10-23 08:33:03
  • asp之家 软件编程 m.aspxhome.com