java分布式事务原理_Springcloud seata分布式事务实现代码解析

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

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

e41a4d09eb69ec7cf534ae7f4f156bcc.png

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

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

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

file.conf,改动两个地方

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

9ce126ad5b17996838671bc32068c072.png

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

c3ca9211f503cd8f05c9774b0dae3be3.png

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

655ed4656bf0e032bb86891591a64bdd.png

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

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

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

具体代码可查看GitHub

关键的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

92feb9dafccece77db93e92eab2ae52f.png

account表的undo记录

96fdf4f9a6899f04ee21293ec611cf15.png

记录的信息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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值