Springcloud seata分布式事務(wù)實(shí)現(xiàn)代碼解析
Seata 是一款開源的分布式事務(wù)解決方案,致力于提供高性能和簡單易用的分布式事務(wù)服務(wù)。本篇不涉及其原理,只用代碼構(gòu)建項(xiàng)目簡單試用一下其回滾的機(jī)制。
大致上seata分為TC,TM,RM三大構(gòu)建成整體。它們之間的包含關(guān)系如下。即一(xid主鍵編碼,記錄信息)帶三(TC,TM,RM)
下面之間構(gòu)建項(xiàng)目進(jìn)行測試。
1.下載seata并解壓,然后改動配置文件。
http://seata.io/zh-cn/blog/download.html官網(wǎng)下載。
解壓之后到conf中修改file和registry文件,修改之前一定記得先備份。
file.conf,改動兩個(gè)地方
將group后面的參數(shù)定義一個(gè)名字,隨意
存儲方式選db放在數(shù)據(jù)庫,自然其配置信息根據(jù)自己的數(shù)據(jù)庫去填寫。
然后是register文件,填寫信息將seata注冊到nacos中。
啟動自然是在bin中打開bat文件即可,注意需要先啟動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ò)誤就會使整個(gè)項(xiàng)目的執(zhí)行過程進(jìn)行回滾,而不是單事務(wù)的回滾。
3.seata回滾原理
在每次注解的方法里進(jìn)行執(zhí)行sql語句的時(shí)候都會創(chuàng)建一個(gè)id記錄此次的寫操作同時(shí)在每次的寫操作前后都會生成前置記錄和后置記錄,可以在出現(xiàn)錯(cuò)誤回滾的時(shí)候,通過記錄進(jìn)行逆操作回滾重新將數(shù)據(jù)寫回去。
通過數(shù)據(jù)庫配置的seata庫展示可以看見對應(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)回滾。
本文只是簡單的配置,后續(xù)會進(jìn)行詳細(xì)補(bǔ)充。
所有的代碼都在GitHub
https://github.com/MaTsukun/springcloud2020
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
自己動手實(shí)現(xiàn)mybatis動態(tài)sql的方法
下面小編就為大家分享一篇自己動手實(shí)現(xiàn)mybatis動態(tài)sql的方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2017-12-12Java多線程之Callable接口的實(shí)現(xiàn)
這篇文章主要介紹了Java多線程之Callable接口的實(shí)現(xiàn),Callable和Runnbale一樣代表著任務(wù),區(qū)別在于Callable有返回值并且可以拋出異常。感興趣的小伙伴們可以參考一下2018-08-08Maven中plugins和pluginManagement區(qū)別小結(jié)
pluginManagement是表示插件聲明,plugins就是直接引入一個(gè)plugin,本文主要介紹了Maven中plugins和pluginManagement區(qū)別小結(jié),具有一定的參考價(jià)值,感興趣的可以了解一下2024-06-06