Seata
Seata 是一款開源的分佈式事務解決方案,致力於在微服務架構下提供高性能和簡單易用的分佈式事務服務。在 Seata 開源之前,Seata 對應的內部版本在阿里經濟體內部一直扮演著分佈式一致性中間件的角色,幫助經濟體平穩的度過歷年的雙11,對各BU業務進行了有力的支撐。經過多年沉澱與積累,商業化產品先後在阿里雲、金融雲進行售賣。2019.1 為了打造更加完善的技術生態和普惠技術成果,Seata 正式宣佈對外開源,未來 Seata 將以社區共建的形式幫助其技術更加可靠與完備。
支持的模式
目前已支持 Dubbo、Spring Cloud、Sofa-RPC、Motan 和 grpc 等RPC框架,其他框架持續集成中。
Seata是一款開源的分佈式事務解決方案,致力於提供高性能和簡單易用的分佈式事務服務。Seata 將為用戶提供了 AT、TCC、SAGA 和 XA 事務模式,為用戶打造一站式的分佈式解決方案。
AT 模式
提供無侵入自動補償的事務模式,目前已支持 MySQL、 Oracle 、PostgreSQL和 TiDB的AT模式,H2 開發中
TCC 模式
支持 TCC 模式並可與 AT 混用,靈活度更高
SAGA 模式
為長事務提供有效的解決方案
XA 模式(開發中)
支持已實現 XA 接口的數據庫的 XA 模式
原理架構
XID:全局唯一的事務ID
TC:Transaction Coordinator,事務協調器,維護全局事務的運行狀態,負責協調並驅動全局事務的提交或者回滾。
TM:Transaction Manager,控制全局事務的邊界,負責啟動一個全局事務,並最終發佈全局的提交或回滾的決議。
RM:Resource Manager,控制分支事務,負責分支註冊,狀態彙報,並接受事務協調的指令,驅動分支(本地)事務的提交或回滾。
Seata AT 模式
先講解AT模式
前提
- 基於支持本地 ACID 事務的關係型數據庫。
- Java 應用,通過 JDBC 訪問數據庫。
整體機制
兩階段提交協議的演變:
- 一階段:業務數據和回滾日誌記錄在同一個本地事務中提交,釋放本地鎖和連接資源。
- 二階段:
- 提交異步化,非常快速地完成。
- 回滾通過一階段的回滾日誌進行反向補償。
以下寫一個Demo演示。
首先下載Seata-server,文章不給放鏈接,真的很坑。在Github中https://github.com/後面加上下面的地址,
seata/seata/releases/tag/v0.9.0。
解壓,seata-server-0.9.0.zip,解壓完成進入conf目錄。
將my_test_tx_group修改為自己自定義的名字,在這裡我改成nb_tx_group。
然後,將mode="file",改為mode="db"。
修改數據庫的url,賬號和密碼。
打開conf目錄下的registry.conf文件,修改type="file",改成type="nacos"。
建立seata庫,建立完成後,導入sql腳本。
導入腳本,生成三個表branch_table,globle_table,lock_table。
接著,啟動nacos,再啟動seata服務端,bin目錄下的seata-server.bat。發現啟動正常。
再新增三個seata_order訂單庫,seata_storage庫存庫,seata_account賬戶庫,用於測試分佈式事務。
create database seata_order;
create database seata_storage;
create database seata_account;
每個庫建立一個業務表,和一個undo_log。
undo_log為官方需要在業務庫建立的一個表。
建表語句見安裝包腳本:
建立三個模塊:
seata-order-service8801,訂單服務
seata-account-service8802,賬戶服務
seata-stoarage-service8803,庫存服務
三個模塊添加依賴:
<code><
dependencies
><
dependency
><
groupId
>com.alibaba.cloudgroupId
><
artifactId
>spring-cloud-starter-alibaba-nacos-discoveryartifactId
>dependency
><
dependency
><
groupId
>com.alibaba.cloudgroupId
><
artifactId
>spring-cloud-starter-alibaba-seataartifactId
><
exclusions
><
exclusion
><
artifactId
>seata-allartifactId
><
groupId
>io.seatagroupId
>exclusion
>exclusions
>dependency
><
dependency
><
groupId
> io.seatagroupId
><
artifactId
>seata-allartifactId
><
version
>0.9.0version
>dependency
><
dependency
><
groupId
>org.springframework.cloudgroupId
><
artifactId
>spring-cloud-starter-openfeignartifactId
>dependency
><
dependency
><
groupId
>org.springframework.bootgroupId
><
artifactId
>spring-boot-starter-webartifactId
>dependency
><
dependency
><
groupId
>org.springframework.bootgroupId
><
artifactId
>spring-boot-starter-actuatorartifactId
>dependency
><
dependency
><
groupId
>mysqlgroupId
><
artifactId
>mysql-connector-javaartifactId
><
version
>5.1.37version
>dependency
><
dependency
><
groupId
>com.alibabagroupId
><
artifactId
>druid-spring-boot-starterartifactId
><
version
>1.1.10version
>dependency
><
dependency
><
groupId
>org.mybatis.spring.bootgroupId
><
artifactId
>mybatis-spring-boot-starter
artifactId
><
version
>2.0.0version
>dependency
><
dependency
><
groupId
>org.springframework.bootgroupId
><
artifactId
>spring-boot-starter-testartifactId
><
scope
>testscope
>dependency
><
dependency
><
artifactId
>springcloud-commomartifactId
><
groupId
>com.learn.springcloudgroupId
><
version
>1.0-SNAPSHOTversion
>dependency
>dependencies
>/<code>
配置文件
<code>server
:port
:8801
spring
:application
:name
: seata-order-servicecloud
:alibaba
:seata
:tx-service-group
: nb_tx_groupnacos
:discovery
:server-addr
:localhost
:8848
#配置Nacos地址datasource
:driver-class-name
: com.mysql.jdbc.Driverurl
:jdbc
:mysql
:username
: rootpassword
:feign
:hystrix
:enabled
: falselogging
:level
:io
:seata
: infomybatis
:mapperLocations
:classpath
:mapper/<code>
將剛才配置好的file.conf,register.conf放入resources目錄下
其他的配置DataSource配置,dao,service,domain,mybatis的配置xml文件
就不貼出來了。
seata-account-service8802,
seata-stoarage-service8803大體配置也差不多,就不貼代碼了。
OrderController有個創建訂單方法,
public class OrderController
{
@Resource
private OrderService orderService;
@GetMapping("/order/create")
public ResultEntity create(Order order)
{
orderService.create(order);
return new ResultEntity(ResultEntity.SUCCESS,"訂單創建成功",ResultEntity.NO_DATA);
}
}
對應的Service實現類:
/**
* 創建訂單->調用庫存服務扣減庫存->調用賬戶服務扣減賬戶餘額->修改訂單狀態
* 簡單說:下訂單->扣庫存->減餘額->改狀態
*/
@Override
public void create(Order order)
{
log.info("----->開始新建訂單");
//1 新建訂單
orderDao.create(order);
//2 扣減庫存
log.info("----->訂單微服務開始調用庫存,做扣減Count");
storageService.decrease(order.getProductId(),order.getCount());
log.info("----->訂單微服務開始調用庫存,做扣減end");
//3 扣減賬戶
log.info("----->訂單微服務開始調用賬戶,做扣減Money");
accountService.decrease(order.getUserId(),order.getMoney());
log.info("----->訂單微服務開始調用賬戶,做扣減end");
//4 修改訂單狀態,從零到1,1代表已經完成
log.info("----->修改訂單狀態開始");
orderDao.update(order.getUserId(),0);
log.info("----->修改訂單狀態結束");
log.info("----->下訂單結束了,O(∩_∩)O哈哈~");
}
}
實現類,模擬用戶下訂單,下訂單->扣庫存->減餘額->改狀態。其中訂單庫,庫存庫,賬戶庫都不是一個數據庫下面,分別有自己的庫。此時在一個業務方法裡,分別調用訂單,庫存,賬戶服務,這樣就出現了分佈式的事務問題。
此時,在account服務的方法裡,模擬一個數字異常。
啟動服務,模擬用戶下訂單請求:
結果訂表入了一條數據
庫存表更改了庫存信息。
賬戶由於異常,數據並未出現變化。
這樣就導致了數據不一致,可能就需要刪庫跑路了。
此時還原數據。在實現類的方法上加一個註解:
name為自己自定義,rollbackFor:出現異常發生回滾。
account有異常,三個表的數據沒有變化,事務回滾。
原理解析
只需在業務方法上加一個@GloableTrancactional註解。
TM開啟分佈式事務(TM向TC註冊全局事務記錄),即@GloableTrancactional處。
按業務場景,編排數據庫、服務等事務內資源(RM向TC彙報資源準備狀態)
TM結束分佈式事務,事務一階段結束(TM通知TC提交/回滾分佈式事務)
TC彙報事務信息,決定分佈式事務是提交還是回滾
TC通知所有RM提交/回滾資源,事務二階段結束
AT模式中:
Seata會攔截業務SQL
一階段加載:
- 解析SQL語義,找到業務SQL需要更新的業務數據,在業務數據被更新前,將其保存成“before image”
- 執行業務SQL更新業務數據
3. 在執行業務數據更新之後,將其保存成“after image”,最後形成行鎖
以上操作全部在一個數據庫事務內完成,這樣保證了一階段操作的原子性。
二階段提交:
二階段順提交的話,因為業務SQL在一階段已經提交至數據庫,所以Seata框架只需要將一階段保存的快照數據和行瑣刪掉,完成數據清理即可。
三階段回滾:
二階段如果回滾的話,Seata就需要回滾一階段已經執行的業務SQL。還原業務數據。回滾方式便是用“before image”還原數據,但在還原數據首先要驗證髒寫,對比,數據當前數據和after image,一致就說明沒有髒寫,可以還原業務數據,如果不一致就說明有髒寫,出現髒寫就需要人工處理。
總結
- seata社區活躍,短短几個月時間star數已經上W,目前已經更新0.8版本,到1.0版本可供線上環境使用。
- 靈活,對於seata的使用而言,使用非常簡單,特別對於AT模式來說,幾乎只要加一個註解就能實現分佈式事務。
- 高性能,雖然對於使用2pc協議的一個最大詬病就是性能問題,多個庫同時鎖定造成性能的急劇下降。 而seata在這個基礎上有較大的提升,特別對於tcc模式而言。
- 目前TC還不支持集群部署,一旦TC宕機,整個系統分佈式事務全都無法處理。
關鍵字: dependency 事務 Seata