Springcloud seata分布式事務實現(xiàn)代碼解析
Seata 是一款開源的分布式事務解決方案,致力于提供高性能和簡單易用的分布式事務服務。本篇不涉及其原理,只用代碼構(gòu)建項目簡單試用一下其回滾的機制。
大致上seata分為TC,TM,RM三大構(gòu)建成整體。它們之間的包含關系如下。即一(xid主鍵編碼,記錄信息)帶三(TC,TM,RM)

下面之間構(gòu)建項目進行測試。
1.下載seata并解壓,然后改動配置文件。
http://seata.io/zh-cn/blog/download.html官網(wǎng)下載。
解壓之后到conf中修改file和registry文件,修改之前一定記得先備份。
file.conf,改動兩個地方
將group后面的參數(shù)定義一個名字,隨意

存儲方式選db放在數(shù)據(jù)庫,自然其配置信息根據(jù)自己的數(shù)據(jù)庫去填寫。

然后是register文件,填寫信息將seata注冊到nacos中。

啟動自然是在bin中打開bat文件即可,注意需要先啟動naco。
2.構(gòu)建項目(order,storage,account)
演示整體的服務調(diào)用還有服務報錯的時候進入回滾。通過創(chuàng)建訂單->檢查庫存并扣除->檢查賬戶并扣除->修改訂單狀態(tài)
具體代碼可查看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.創(chuàng)建訂單
log.info("開始創(chuàng)建訂單");
orderMapper.create(order);
//2.減少庫存
log.info("查詢庫存并且進行更改");
storageService.decrease(order.getProductId(),order.getCount());
//3.扣除費用
log.info("查詢余額并扣除費用");
accountService.updateAccount(order.getUserId(),order.getMoney());
//4.修改狀態(tài)
log.info("更改訂單狀態(tài)");
orderMapper.update(order.getUserId(),0);
log.info("訂單結(jié)束,O(∩_∩)O哈哈~");
}
}
可以看到在order項目中同時調(diào)用了storage和account的項目的方法,采用的是openfeign,整體形成了一個鏈路,成為一個整的事務。
而添加的GlobalTransactional注解則保證了事務中任何一方出現(xiàn)錯誤就會使整個項目的執(zhí)行過程進行回滾,而不是單事務的回滾。
3.seata回滾原理
在每次注解的方法里進行執(zhí)行sql語句的時候都會創(chuàng)建一個id記錄此次的寫操作同時在每次的寫操作前后都會生成前置記錄和后置記錄,可以在出現(xiàn)錯誤回滾的時候,通過記錄進行逆操作回滾重新將數(shù)據(jù)寫回去。
通過數(shù)據(jù)庫配置的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快照記錄,通過這些記錄可以實現(xiàn)逆操作,重新寫進數(shù)據(jù)實現(xiàn)回滾。
本文只是簡單的配置,后續(xù)會進行詳細補充。
所有的代碼都在GitHub
https://github.com/MaTsukun/springcloud2020
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
自己動手實現(xiàn)mybatis動態(tài)sql的方法
下面小編就為大家分享一篇自己動手實現(xiàn)mybatis動態(tài)sql的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2017-12-12
Maven中plugins和pluginManagement區(qū)別小結(jié)
pluginManagement是表示插件聲明,plugins就是直接引入一個plugin,本文主要介紹了Maven中plugins和pluginManagement區(qū)別小結(jié),具有一定的參考價值,感興趣的可以了解一下2024-06-06

