Springcloud seata分布式事务实现代码解析
作者:梦泽千秋 发布时间:2022-12-27 20:14:01
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。本篇不涉及其原理,只用代码构建项目简单试用一下其回滚的机制。
大致上seata分为TC,TM,RM三大构建成整体。它们之间的包含关系如下。即一(xid主键编码,记录信息)带三(TC,TM,RM)
下面之间构建项目进行测试。
1.下载seata并解压,然后改动配置文件。
http://seata.io/zh-cn/blog/download.html官网下载。
解压之后到conf中修改file和registry文件,修改之前一定记得先备份。
file.conf,改动两个地方
将group后面的参数定义一个名字,随意
存储方式选db放在数据库,自然其配置信息根据自己的数据库去填写。
然后是register文件,填写信息将seata注册到nacos中。
启动自然是在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
account表的undo记录
记录的信息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提供了@Scheduled注解,方便进行定时任务的开发概述要使用@Scheduled注解,
- java.math.BigDecimal及加减乘除计算BigDecimal简介BigDecimal用来对需要更大或更小的数进行任意精度的运算
- 1. Java对象结构Java对象结构包括三部分:对象头、对象体和填充字节,如图所示:对象头又包括三个字段:第一个字段叫作Mark Word
- 1. 开方:Math.sqrt(x);2. x的a方:Math.pow(x,a);3. 绝对值:Math.abs(x);4. BigInte
- 一、Thread 类了解如何使用Thread 类实现多线程之后,继续学习Thread 类实现多线程之后的相关功能及方法。1、操作线程名称的方
- 前言前天工作中遇到了这样一个问题,我在接口的参数封装了一个pojo,这是很常见的,当参数一多,惯性的思维就是封装一个pojo.那么在参数前有
- 概述线上项目发布一般有以下几种方案:机发布蓝绿部署滚动部署灰度发布停机发布 这种发布一般在夜里或者进行大版本升级的时候发布,因为需要停机,所
- Java定义Long数据类型Long lg=10L;只需要在定义的的整型后面加个L;就和定义float数据类型一样Float ft=5.20
- springboot项目没有mainClass实现打包运行项目分为两个部分,一个是业务代码模块,一个是框架模块,运行class放在框架部分,
- 目录操作创建目录File类中有两个方法可以用来创建文件夹:mkdir( )方法创建一个文件夹,成功则返回true,失败则返回false。失败
- 以下四种方式:1.继承Thread类,重写run方法2.实现Runnable接口,重写run方法,实现Runnable接口的实现类的实例对象
- 本文实例讲述了java实现单词搜索迷宫游戏。分享给大家供大家参考。具体分析如下:我们在杂志上,经常能够看到找单词的小游戏,在一个二维表格中,
- 一:form在前台以post方式提交数据: 浏览器将数据(假设为“中国”)发送给服务器的时
- SpringBoot实践,开发社区登录模块今日份开启 发送邮件三个步骤:1、邮箱设置首先需要注册一个新浪邮箱 18215626061@sin
- Android手势解锁本文讲述的是一个手势解锁的库,可以定制显示隐藏宫格点、路径、并且带有小九宫格显示图,和震动!让你学会使用这个简单,高效
- 一个是新浪微博,腾讯微博的分享按钮,一个是他们的绑定情况(其实就是是否授权)。点击微博分享中新浪或腾讯按钮,就进行相应的授权(若没授权),显
- 利用Java连接MySQL做登陆界面,供大家参考,具体内容如下1、首先需要建立一个类,在这里,我命名为newLoginnewLogin类的代
- 添加jar包这里的Scala不是maven工程所以要找到项目结构(快捷键:同时按住Ctrl+shift+Alt+s)在模块里面添加添加MyS
- 很多童鞋反应在吧项目导入到eclipse(myeclipse)时中文会有乱码,修改了编码格式后还是乱码,这里给大家介绍一下关于中文乱码时修改
- Guava Cache:⾕歌开源缓存框架Guava Cache是在内存中缓存数据,相比较于数据库或redis存储,访问内存中的数据会更加高效