濟南網(wǎng)站外包友鏈查詢站長工具
前言
該博客為Sentinel
學(xué)習(xí)筆記,主要目的是為了幫助后期快速復(fù)習(xí)使用
學(xué)習(xí)視頻:7小快速通關(guān)SpringCloud
輔助文檔:SpringCloud快速通關(guān)
源碼地址:cloud-demo
一、簡介
官網(wǎng):https://seata.apache.org/zh-cn/
Seata
是一款開源的分布式事務(wù)解決方案,旨在解決微服務(wù)架構(gòu)中跨服務(wù)的事務(wù)一致性問題。它支持多種事務(wù)模型(如AT、TCC、SAGA),能夠保證跨多個服務(wù)和數(shù)據(jù)庫的數(shù)據(jù)一致性,簡化了分布式事務(wù)的管理。
為什么需要使用 Seata?
在分布式系統(tǒng)中,由于涉及多個微服務(wù)和多個數(shù)據(jù)庫,事務(wù)的一致性和可靠性成為了一個挑戰(zhàn)。傳統(tǒng)的事務(wù)管理機制(如兩階段提交)不適用于復(fù)雜的微服務(wù)環(huán)境,因為它們的成本高、實現(xiàn)復(fù)雜且不易擴展。
Seata 的優(yōu)勢體現(xiàn)在以下幾個方面:
- 分布式事務(wù)支持:
Seata
提供了跨多個服務(wù)和數(shù)據(jù)庫的事務(wù)支持,確保多個服務(wù)的操作能夠像一個單一事務(wù)一樣進(jìn)行提交或回滾。 - 性能優(yōu)化:
Seata
采用高效的事務(wù)協(xié)調(diào)機制,通過減少分布式事務(wù)處理的開銷,顯著提升系統(tǒng)性能。 - 可靠性和容錯性:
Seata
的設(shè)計保證了事務(wù)的一致性和可靠性,即使在部分節(jié)點發(fā)生故障時,也能保證系統(tǒng)的正確性。 - 簡化開發(fā):使用
Seata
時,開發(fā)者不需要自己編寫復(fù)雜的分布式事務(wù)處理邏輯,Seata 提供了易用的注解和API,極大地簡化了開發(fā)工作。 - 靈活的事務(wù)模型:
Seata
提供了多種事務(wù)模型(AT
、TCC
、SAGA
),可以根據(jù)具體業(yè)務(wù)場景選擇合適的事務(wù)管理方式,滿足不同的需求。
二、快速入門
2.1 環(huán)境準(zhǔn)備
2.2 原理
2.2.1 核心組件
Seata
分布式事務(wù)的工作原理主要涉及三個核心組件:事務(wù)協(xié)調(diào)器(TC
)、事務(wù)管理器(TM
)和資源管理器(RM
)。以下是這些組件的詳細(xì)工作原理:
- 事務(wù)協(xié)調(diào)器(TC):
TC
負(fù)責(zé)維護(hù)全局事務(wù)的狀態(tài),并協(xié)調(diào)事務(wù)的提交或回滾。 它接收全局事務(wù)的開始、提交或回滾請求,協(xié)調(diào)各個服務(wù)的事務(wù)執(zhí)行。TC在收到所有分支事務(wù)的提交或回滾確認(rèn)后,將全局事務(wù)狀態(tài)標(biāo)記為結(jié)束。 - 事務(wù)管理器(TM):
TM
負(fù)責(zé)定義和發(fā)起全局事務(wù)。 當(dāng)TM發(fā)起一個全局事務(wù)時,TC創(chuàng)建一個全局事務(wù)ID,并將該事務(wù)狀態(tài)設(shè)置為“開始”狀態(tài)。TM通知TC提交事務(wù),TC依次通知各RM提交其分支事務(wù)。如果任一分支事務(wù)執(zhí)行失敗或出現(xiàn)異常,TM通知TC回滾事務(wù),TC會依次通知各RM進(jìn)行回滾操作。 - 資源管理器(RM):
RM
負(fù)責(zé)管理分支事務(wù)(Branch Transaction),并與本地數(shù)據(jù)庫資源交互,確保各個分支事務(wù)的提交和回滾操作。 在全局事務(wù)的生命周期內(nèi),多個服務(wù)會分別執(zhí)行屬于自己的本地事務(wù)(即分支事務(wù))。每個分支事務(wù)在執(zhí)行前需要向TC注冊,并綁定到全局事務(wù)ID上。每個服務(wù)的RM負(fù)責(zé)管理分支事務(wù)的執(zhí)行,并與本地資源交互。執(zhí)行完成后,RM會向TC報告分支事務(wù)的執(zhí)行結(jié)果。
2.2.1 實現(xiàn)步驟
Seata
的分布式事務(wù)管理通過以下步驟實現(xiàn):
- 全局事務(wù)開始:當(dāng)TM發(fā)起一個全局事務(wù)時,TC創(chuàng)建一個全局事務(wù)ID,并將該事務(wù)狀態(tài)設(shè)置為“開始”狀態(tài)。
- 注冊分支事務(wù):在全局事務(wù)的生命周期內(nèi),多個服務(wù)會分別執(zhí)行屬于自己的本地事務(wù)(即分支事務(wù))。每個分支事務(wù)在執(zhí)行前需要向TC注冊,并綁定到全局事務(wù)ID上。
- 分支事務(wù)執(zhí)行:每個服務(wù)的RM負(fù)責(zé)管理分支事務(wù)的執(zhí)行,并與本地資源交互。執(zhí)行完成后,RM會向TC報告分支事務(wù)的執(zhí)行結(jié)果。
- 全局提交或回滾:當(dāng)所有分支事務(wù)都執(zhí)行成功時,TM通知TC提交事務(wù),TC依次通知各RM提交其分支事務(wù)。如果任一分支事務(wù)執(zhí)行失敗或出現(xiàn)異常,TM通知TC回滾事務(wù),TC會依次通知各RM進(jìn)行回滾操作。
- 事務(wù)結(jié)束:TC在收到所有分支事務(wù)的提交或回滾確認(rèn)后,將全局事務(wù)狀態(tài)標(biāo)記為結(jié)束。
2.3 seata-server
- 下載:https://seata.apache.org/zh-cn/download/seata-server
- 解壓并啟動:
seata-server.bat
Server端口【TC事務(wù)協(xié)調(diào)者】:8091
Web可視化界面:http://localhost:7091
,賬號和密碼都為seata
2.4 微服務(wù)配置
2.4.1 引依賴
<!-- 分布式事務(wù)Seata --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId></dependency>
2.4.2 配置
每個微服務(wù)創(chuàng)建 file.conf
文件,完整內(nèi)容如下;
【微服務(wù)只需要復(fù)制 service 塊配置即可】
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#transport {# tcp, unix-domain-sockettype = "TCP"#NIO, NATIVEserver = "NIO"#enable heartbeatheartbeat = true# the tm client batch send request enableenableTmClientBatchSendRequest = false# the rm client batch send request enableenableRmClientBatchSendRequest = true# the rm client rpc request timeoutrpcRmRequestTimeout = 2000# the tm client rpc request timeoutrpcTmRequestTimeout = 30000# the rm client rpc request timeoutrpcRmRequestTimeout = 15000#thread factory for nettythreadFactory {bossThreadPrefix = "NettyBoss"workerThreadPrefix = "NettyServerNIOWorker"serverExecutorThread-prefix = "NettyServerBizHandler"shareBossWorker = falseclientSelectorThreadPrefix = "NettyClientSelector"clientSelectorThreadSize = 1clientWorkerThreadPrefix = "NettyClientWorkerThread"# netty boss thread sizebossThreadSize = 1#auto default pin or 8workerThreadSize = "default"}shutdown {# when destroy server, wait secondswait = 3}serialization = "seata"compressor = "none"
}
service {#transaction service group mappingvgroupMapping.default_tx_group = "default"#only support when registry.type=file, please don't set multiple addressesdefault.grouplist = "127.0.0.1:8091"#degrade, current not supportenableDegrade = false#disable seatadisableGlobalTransaction = false
}client {rm {asyncCommitBufferLimit = 10000lock {retryInterval = 10retryTimes = 30retryPolicyBranchRollbackOnConflict = true}reportRetryCount = 5tableMetaCheckEnable = falsetableMetaCheckerInterval = 60000reportSuccessEnable = falsesagaBranchRegisterEnable = falsesagaJsonParser = "fastjson"sagaRetryPersistModeUpdate = falsesagaCompensatePersistModeUpdate = falsetccActionInterceptorOrder = -2147482648 #Ordered.HIGHEST_PRECEDENCE + 1000sqlParserType = "druid"branchExecutionTimeoutXA = 60000connectionTwoPhaseHoldTimeoutXA = 10000}tm {commitRetryCount = 5rollbackRetryCount = 5defaultGlobalTransactionTimeout = 60000degradeCheck = falsedegradeCheckPeriod = 2000degradeCheckAllowTimes = 10interceptorOrder = -2147482648 #Ordered.HIGHEST_PRECEDENCE + 1000}undo {dataValidation = trueonlyCareUpdateColumns = truelogSerialization = "jackson"logTable = "undo_log"compress {enable = true# allow zip, gzip, deflater, lz4, bzip2, zstd default is ziptype = zip# if rollback info size > threshold, then will be compress# allow k m g tthreshold = 64k}}loadBalance {type = "XID"virtualNodes = 10}
}
log {exceptionRate = 100
}
tcc {fence {# tcc fence log table namelogTableName = tcc_fence_log# tcc fence log clean periodcleanPeriod = 1h}
}
2.4.3 開啟全局事務(wù)
在最大的方法入口,標(biāo)記@GlobalTransactional
,即可開啟全局事務(wù)
2.5 二階提交協(xié)議原理
第一階段:本地事務(wù)
- 全局事務(wù)開始:業(yè)務(wù)層發(fā)起全局事務(wù)請求,事務(wù)協(xié)調(diào)者(
TC
)注冊全局事務(wù)并生成全局事務(wù)ID。 - 分支事務(wù)注冊:各個分支事務(wù)(如扣減庫存、創(chuàng)建訂單、扣減余額)分別向
TC
注冊,申請必要的資源鎖(如數(shù)據(jù)庫表的記錄鎖)。 - 執(zhí)行本地事務(wù):
- 扣減庫存:扣減庫存:執(zhí)行SQL
update storage_tbl set count = count - 2 where commodity_code = 'P0001'
,更新庫存數(shù)量。 - 創(chuàng)建訂單:執(zhí)行訂單創(chuàng)建相關(guān)的本地事務(wù)。
- 扣減余額:執(zhí)行扣減賬戶余額的本地事務(wù)。
- 扣減庫存:扣減庫存:執(zhí)行SQL
- 記錄前后鏡像:在執(zhí)行業(yè)務(wù)SQL前后,分別查詢并記錄數(shù)據(jù)的前后鏡像,用于生成回滾日志(
UNDO LOG
)。 - 插入回滾日志:將前后鏡像組成的回滾日志插入到
UNDO LOG
表中,以便在需要時進(jìn)行數(shù)據(jù)回滾。 - 本地事務(wù)提交:將業(yè)務(wù)數(shù)據(jù)和
UNDO LOG
一起保存,并匯報自己提交成功與否的結(jié)果。
第二階段:提交階段
- 分支提交:
- 收到
TC
的提交請求后,各個分支事務(wù)立即響應(yīng)OK
,表示準(zhǔn)備就緒。 - 給異步任務(wù)隊列中添加異步任務(wù),異步和批量地刪除相應(yīng)的
UNDO LOG
記錄。
- 收到
- 分支回滾:
- 如果TC的提交請求未能成功,或者在第一階段中某個分支事務(wù)失敗,
TC
將發(fā)起回滾請求。 - 各個分支事務(wù)收到回滾請求后,開啟一個本地事務(wù),執(zhí)行以下任務(wù):
- 找到對應(yīng)的UNDO LOG記錄。
- 數(shù)據(jù)校驗:比較后鏡像與當(dāng)前數(shù)據(jù),如果不一致,則說明數(shù)據(jù)被其他渠道修改,需要進(jìn)行相應(yīng)處理;如果一致,則執(zhí)行回滾。
- 回滾數(shù)據(jù):根據(jù)
UNDO LOG
的前鏡像內(nèi)容執(zhí)行數(shù)據(jù)修改,完成后刪除UNDO LOG
。
- 如果TC的提交請求未能成功,或者在第一階段中某個分支事務(wù)失敗,
通過以上流程,Seata
確保了分布式事務(wù)的原子性,即所有分支事務(wù)要么全部成功提交,要么全部回滾,從而保證了數(shù)據(jù)的一致性。
2.6 二階提交協(xié)議可視化
2.6.1 觀察初始數(shù)據(jù)庫
2.6.2 設(shè)置斷點
我們在業(yè)務(wù)服務(wù)BusinessServiceImpl
的主方法purchase
中給遠(yuǎn)程調(diào)用方法deduct
和create
加上斷點
并在訂單服務(wù)OrderServiceImpl
的create方法中添加一個除零異常,并在前面加上一個斷點
2.6.3 發(fā)起請求,觀察數(shù)據(jù)變化
在瀏覽器發(fā)送請求,http://localhost:11000/purchase?userId=1&commodityCode=P0001&count=2
這里,我們來到第一個斷點扣減庫存deduct
,可以看到控制臺打印開啟了一個新的全局事務(wù)
此時在Seata
控制臺中可以觀測到這個全局事務(wù)
當(dāng)前沒有任何數(shù)據(jù)提交,即沒有全局鎖
->
只有第一階段本地提交,要修改數(shù)據(jù)之前,才會申請自己數(shù)據(jù)的記錄鎖,即全局鎖
繼續(xù)放行到第二個斷點位置,創(chuàng)建訂單create
,觀察Seata
控制臺,開啟分支事務(wù)并查看
可以看到storage_db
的分支事務(wù)
而且在全局鎖中也可以看到數(shù)據(jù) -> 分支事務(wù)一定會提交一個數(shù)據(jù)
繼續(xù)觀察storage_db
數(shù)據(jù)庫,可以看到庫存已經(jīng)被扣減,并且在undo_log
表中有一條記錄
查看rollback_info
字段的內(nèi)容,
在前鏡像" beforeImage "
中可以看到,在記錄修改之前的值為100 "value": 100
后鏡像"afterImage"
,扣減庫存后的數(shù)據(jù)為,"value": 98
{"@class": "org.apache.seata.rm.datasource.undo.BranchUndoLog","xid": "192.168.4.105:8091:7782838703486791689","branchId": 7782838703486791690,"sqlUndoLogs": ["java.util.ArrayList",[{"@class": "org.apache.seata.rm.datasource.undo.SQLUndoLog","sqlType": "UPDATE","tableName": "storage_tbl","beforeImage": {"@class": "org.apache.seata.rm.datasource.sql.struct.TableRecords","tableName": "storage_tbl","rows": ["java.util.ArrayList",[{"@class": "org.apache.seata.rm.datasource.sql.struct.Row","fields": ["java.util.ArrayList",[{"@class": "org.apache.seata.rm.datasource.sql.struct.Field","name": "count","keyType": "NULL","type": 4,"value": 100},{"@class": "org.apache.seata.rm.datasource.sql.struct.Field","name": "id","keyType": "PRIMARY_KEY","type": 4,"value": 1}]}}]]},"afterImage": {"@class": "org.apache.seata.rm.datasource.sql.struct.TableRecords","tableName": "storage_tbl","rows": ["java.util.ArrayList",[{"@class": "org.apache.seata.rm.datasource.sql.struct.Row","fields": ["java.util.ArrayList",[{"@class": "org.apache.seata.rm.datasource.sql.struct.Field","name": "count","keyType": "NULL","type": 4,"value": 98},{"@class": "org.apache.seata.rm.datasource.sql.struct.Field","name": "id","keyType": "PRIMARY_KEY","type": 4,"value": 1}]}}]]}}]]
}
繼續(xù)放行到第三個斷點位置,保存訂單orderTblMapper.insert(orderTbl);
,觀察Seata
控制臺
可以看到多了account_db
的分支事務(wù)和全局鎖
此時數(shù)據(jù)庫的余額和庫存均被扣減,并且生成了UNDO LOG
記錄
此時繼續(xù)放行,即會執(zhí)行int i = 1/0;
拋出除零異常,所有數(shù)據(jù)全部按照前鏡像回滾,UNDO LOG
記錄
注意:事務(wù)事務(wù)超時時間為一分鐘,調(diào)試過程中,一旦超時會報一下錯誤
may be has finished
->
事務(wù)可能完成
branch register failed
->
分支注冊事務(wù)失敗
2025-02-13T22:41:16.010+08:00 ERROR 19556 --- [seata-storage] [io-13000-exec-9] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.transaction.TransactionSystemException: JDBC commit failed] with root causeorg.apache.seata.core.exception.RmTransactionException: branch register failed, xid: 192.168.4.105:8091:7782838703486791747, errMsg: TransactionException[Could not found global transaction xid = 192.168.4.105:8091:7782838703486791747, may be has finished.]
2.7 四種事務(wù)模式
2.7.1 AT模式
這是Seata
中最常用的一種分布式事務(wù)模式,它通過自動的預(yù)提交、回滾操作,實現(xiàn)分布式事務(wù)的透明化管理。AT
模式的核心在于自動處理數(shù)據(jù)庫的事務(wù)操作,不需要對業(yè)務(wù)代碼進(jìn)行侵入性的修改。
2.7.2 TCC模式
適用于需要顯式控制分布式事務(wù)的場景,確保所有參與者都遵循統(tǒng)一的規(guī)則。TCC
模式通過三階段的提交(Try、Confirm、Cancel)來保證最終一致性。適用于夾雜非數(shù)據(jù)庫操作的事務(wù)。
2.7.3 Saga模式
適用于復(fù)雜的業(yè)務(wù)流程,每個步驟都可以獨立地進(jìn)行業(yè)務(wù)邏輯處理。在Saga
模式中,業(yè)務(wù)流程中每個參與者都提交本地事務(wù),當(dāng)出現(xiàn)某一個參與者失敗則補償前面已經(jīng)成功的參與者,一階段正向服務(wù)和二階段補償服務(wù)都由業(yè)務(wù)開發(fā)實現(xiàn)。
2.7.4 XA模式
適用于需要兼容傳統(tǒng)數(shù)據(jù)庫系統(tǒng)的場景,支持多種資源管理器。XA模式是強一致性分階段事務(wù)模式,犧牲了一定的可用性,無業(yè)務(wù)侵入。
2.7.5 切換模式
添加application.yml
配置
seata:data-source-proxy-mode: AT #默認(rèn)為AT模式