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

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

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

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

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

