這兩天在搞這個分佈式事務,發現網上的seata資料大多數都是樂色,啥都不是,可能他都不知道register.conf和file.conf配置是幹啥的,最後本人提供真實可用Demo,有問題可以私聊.
前一章節提過 2PC有兩種實現一種是XA,XA是傳統分佈式事務解決方案,基於數據庫必須要支持2PC協議,利用數據的2PC協議來實現分佈式事務,而一種是seata,但seata同時支持AT和TCC,這裏seata實現的AT,AT和XA執行流程一樣,都是先準備提交,最後提交還是回滾都是有事務協調器來決定,而TCC模式是在預備階段就以及提交事務,釋放鎖資源.
什麼是2PC兩階段提交
2PC即兩階段提交協議,是將整個事務流程分爲兩個階段,準備階段(Prepare phase)、提交階段(commit phase).
2是指兩個階段,P是指準備階段,C是指提交階段。
舉例:張三和李四好久不見,老友約起聚餐,飯店老闆要求先買單,才能出票。這時張三和李四分別抱怨近況不如
意,囊中羞澀,都不願意請客,這時只能AA。只有張三和李四都付款,老闆才能出票安排就餐。但由於張三和李四 都是鐵公雞,形成了尷尬的一幕:
準備階段:老闆要求張三付款,張三付款。老闆要求李四付款,李四付款。 提交階段:老闆出票,兩人拿票紛紛落座就餐。
例子中形成了一個事務,若張三或李四其中一人拒絕付款,或錢不夠,店老闆都不會給出票,並且會把已收款退 回。
整個事務過程由事務管理器和參與者組成,店老闆就是事務管理器,張三、李四就是事務參與者,事務管理器負責
決策整個分佈式事務的提交和回滾,事務參與者負責自己本地事務的提交和回滾。
在計算機中部分關係數據庫如Oracle、MySQL支持兩階段提交協議,如下圖:
- 準備階段(Prepare phase):事務管理器給每個參與者發送Prepare消息,每個數據庫參與者在本地執行事 務,並寫本地的Undo/Redo日誌,此時事務沒有提交。
(Undo日誌是記錄修改前的數據,用於數據庫回滾,Redo日誌是記錄修改後的數據,用於提交事務後寫入數 據文件)- 提交階段(commit phase):如果事務管理器收到了參與者的執行失敗或者超時消息時,直接給每個參與者 發送回滾(Rollback)消息;否則,發送提交(Commit)消息;參與者根據事務管理器的指令執行提交或者回滾操
作,並釋放事務處理過程中使用的鎖資源。注意:必須在最後階段釋放鎖資源。 下圖展示了2PC的兩個階段,分成功和失敗兩個情況說明: 成功情況:
2PC主流的兩種方案XA和Seata方案
- XA
這裏就不講解了,傳統xa就是案例上將的
prepare階段事務沒提交,需要由事務管理器來確認事務是否提交,這樣導致傳統2PC會一直持有資源鎖,性能差
- Seata
是由阿里中間件團隊發起的開源項目 Fescar,後更名爲Seata,它是一個是開源的分佈式事務框架。
傳統2PC的問題在Seata中得到了解決,它通過對本地關係數據庫的分支事務的協調來驅動完成全局事務,是工作
在應用層的中間件。主要優點是性能較好,且不長時間佔用連接資源,它以高效並且對業務0侵入的方式解決微服務場景下面臨的分佈式事務問題,它目前提供AT模式(即2PC)及TCC模式的分佈式事務解決方案
Seata把一個分佈式事務理解成一個包含了若干分支事務的全局事務。全局事務的職責是協調其下管轄的分支事務
達成一致,要麼一起成功提交,要麼一起失敗回滾。此外,通常分支事務本身就是一個關係數據庫的本地事務,下 圖是全局事務與分支事務的關係圖:
Seata實現2PC與傳統2PC的差別: 架構層次方面,傳統2PC方案的 RM 實際上是在數據庫層,RM 本質上就是數據庫自身,通過 XA協議實現,而 Seata的 RM 是以jar包的形式作爲中間件層部署在應用程序這一側的。
兩階段提交方面,傳統2PC無論第二階段的決議是commit還是rollback,事務性資源的鎖都要保持到Phase2完成
才釋放。而Seata的做法是在Phase1 就將本地事務提交,這樣就可以省去Phase2持鎖的時間,整體提高效率。
seata實現AT事務
本示例通過Seata中間件實現分佈式事務,模擬三個賬戶的轉賬交易過程。
兩個賬戶在三個不同的銀行(張三在bank1、李四在bank2),bank1和bank2是兩個個微服務。交易過程是,張三 給李四轉賬指定金額。
上述交易步驟,要麼一起成功,要麼一起失敗,必須是一個整體性的事務。
交互流程如下: 1、請求bank1進行轉賬,傳入轉賬金額。 2、bank1減少轉賬金額,調用bank2,傳入轉賬金額。
-
首先要下載事務協調器:
https://github.com/seata/seata/releases/download/v0.7.1/seata-server-0.7.1.zip
-
解壓啓動:
seata-server.bat -p 8888 -m file
注:其中8888爲服務端口號;file爲啓動模式,這裏指seata服務將採用文件的方式存儲信息。
如上圖出現“Server started…”的字樣則表示啓動成功。 -
創建mysql
兩個庫 bank1和bank2 都創建account_info 和undo_log表
CREATE TABLE `account_info` (
`id` bigint(20) DEFAULT NULL COMMENT 'id',
`account_name` varchar(100) DEFAULT NULL COMMENT '戶主姓名',
`account_no` varchar(100) DEFAULT NULL COMMENT '戶主id',
`account_password` varchar(100) DEFAULT NULL COMMENT '帳戶密碼',
`account_balance` double DEFAULT NULL COMMENT '帳戶餘額'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='賬戶表'
CREATE TABLE `undo_log` (
`id` bigint(20) DEFAULT NULL COMMENT 'id',
`branch_id` varchar(100) DEFAULT NULL COMMENT '分支id',
`xid` varchar(100) DEFAULT NULL COMMENT '事務id',
`context` varchar(100) DEFAULT NULL COMMENT '內容',
`rollback_info` double DEFAULT NULL COMMENT '回滾信息',
`log_status` double DEFAULT NULL COMMENT '日誌狀態',
`log_created` double DEFAULT NULL COMMENT '日誌創建',
`log_modified` double DEFAULT NULL COMMENT '日誌修改',
`ext` double DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='undo log表'
注意 undo_log名稱以及字段不可修改,這個是seata 的undo日誌,在上面流程中提過,Undo日誌是記錄修改前的數據,用於數據庫回滾,Redo日誌是記錄修改後的數據,用於提交事務後寫入數 據文件
- 創建SpringCloud工程,我這裏使用spring全家桶,你也可以用阿里cloud全家桶
我這裏使用SpringCloud最新版Hoxton.SR5
SpringBoot使用2.3.1.RELEASE
這裏就提示以下代碼的作用以及關鍵處如何配置,源代碼地址: https://github.com/cyf0477/seata-parent
說明一下注冊中心還是Eureka的註冊中心,沒有任何改變
1.在bank1和bank2裏分別加入seata依賴
2.在resource下加入registry.conf,這個conf需要在之前下載的seata服務器資源裏的conf目錄下copy
這裏面需要關注兩個文件,一個是register.conf,一個是file.conf ,這裏就先講講這個register.conf和file.conf的作用,
- register.conf: 用於配置seata負載均衡方式,seata是分佈式事務協調器,既然是分佈式那麼要實現高可用,必然需要有註冊中心,下面就是默認register.conf內容,裏面加有詳細註釋
registry {
#註冊中心選用的方式,默認是file,可以使用nacos ,eureka上面註釋列舉的所有方式都支持
#注意,如果這裏註冊中心原則使用file方式,那麼就必須使用file.conf來配置file的一些配置,而且file模式不支持高可用
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "file" 只能建立一個seata server
nacos {
serverAddr = "localhost"
namespace = "public"
cluster = "default"
}
eureka {
serviceUrl = "http://localhost:1001/eureka" #這裏我用的eureka,那麼只需要修改eureka就可以
application = "default" #seata註冊到eureka的名稱
weight = "1" #權重
}
redis {
serverAddr = "localhost:6379"
db = "0"
}
zk {
cluster = "default"
serverAddr = "127.0.0.1:2181"
session.timeout = 6000
connect.timeout = 2000
}
consul {
cluster = "default"
serverAddr = "127.0.0.1:8500"
}
etcd3 {
cluster = "default"
serverAddr = "http://localhost:2379"
}
sofa {
serverAddr = "127.0.0.1:9603"
application = "default"
region = "DEFAULT_ZONE"
datacenter = "DefaultDataCenter"
cluster = "default"
group = "SEATA_GROUP"
addressWaitTime = "3000"
}
file {
name = "file.conf"
}
}
config { //conf只有下面列舉的才需要修改 如果使用的eureka 下面配置無用
# file、nacos 、apollo、zk、consul、etcd3
type = "file"
nacos {
serverAddr = "localhost"
namespace = "public"
cluster = "default"
}
consul {
serverAddr = "127.0.0.1:8500"
}
apollo {
app.id = "seata-server"
apollo.meta = "http://192.168.1.204:8801"
}
zk {
serverAddr = "127.0.0.1:2181"
session.timeout = 6000
connect.timeout = 2000
}
etcd3 {
serverAddr = "http://localhost:2379"
}
file {
name = "file.conf"
}
}
- file.conf: seata 註冊中心指定爲file file.conf纔有意義,並且如果指定爲file 類型,那麼seata不支持高可用!,這裏就不講解file.conf裏面的內容了,畢竟沒一點意義
- 修改yml配置
spring:
cloud:
alibaba:
seata:
tx-service-group: my_test_tx_group #seata分組
seata:
service:
grouplist:
default: ubuntu1804.wsl:8091 #seata服務地址 可以是多個 key的值我查看源碼裏沒有要求指定什麼名字,這裏就隨意了,如果seata集羣,可以寫多個服務地址
bank1 代表案例中的張三,先說一下數據源配置,讓seata代理數據源,io.seata.rm.datasource.DataSourceProxy
,這樣seata纔可能完全管理事務狀態
需要在主事物分支開啓@GlobalTransactional
註解,在其他子事物分子是不需要這個註解
張三service
李四service
此時 seata at教程就完畢了 到這裏有人可能有點混淆AT和XA AT是在seata裏的概念等同於XA 都是兩階段提交解決方案
tcc解決方案使用hmily下章節講解