分布式事務seata的AT模式介紹

分布式事務seata的AT模式介紹

seata是阿里開源的一款分布式事務解決方案,致力于提供高性能和簡單易用的分布式事務服務。Seata 將為用戶提供了 AT、TCC、SAGA 和 XA 事務模式,本文主要介紹AT模式的使用。

seata安裝

下載seata服務,官方地址:https://github.com/seata/seata/releases
在Linux下,下載完成后,直接解壓,通過命令安裝即可:

sh ./bin/seata-server.sh

支持的啟動參數

參數全寫作用備注
-h–host指定在注冊中心注冊的 IP不指定時獲取當前的 IP,外部訪問部署在云環境和容器中的 server 建議指定
-p–port指定 server 啟動的端口默認為 8091
-m–storeMode事務日志存儲方式支持file和db,默認為 file
-n–serverNode用于指定seata-server節點ID,如 1,2,3…, 默認為 1
-e–seataEnv指定 seata-server 運行環境如 dev, test 等, 服務啟動時會使用 registry-dev.conf 這樣的配置

如:

sh ./bin/seata-server.sh -p 8091 -h 127.0.0.1 -m file

seata的AT模式介紹

AT模式實質是兩階段提交協議的演變,具體如下:

  • 一階段:業務數據和回滾日志記錄在同一個本地事務中提交,釋放本地鎖和連接資源
  • 二階段:
    提交異步化,非常快速地完成。

回滾通過一階段的回滾日志進行反向補償。

業務背景:
用戶調用系統A的store服務,store服務調用系統B的company服務,company服務會新增一條數據,然后把companyId返回系統A,然后系統A通過companyId再新增一條store數據。

一般如果store服務執行失敗了,直接拋異常了,所以company服務也不會執行,
但如果store服務執行成功了,已經寫了一條數據到數據庫,執行company服務時失敗了,就會產生數據不一致的問題。

使用seata的AT模式,主要分為下面幾個步驟:

  • 配置seata服務及創建事務表
  • 調用方配置(對應上面的store服務)
  • 服務提供方配置(對應上面的company服務)

配置seata服務及創建事務表

配置conf/file.conf文件

store {mode = "db" //修改為db模式,標識事務信息用db存儲file {dir = "sessionStore"maxBranchSessionSize = 16384maxGlobalSessionSize = 512fileWriteBufferCacheSize = 16384sessionReloadReadSize = 100flushDiskMode = async}db {datasource = "druid"dbType = "mysql"driverClassName = "com.mysql.cj.jdbc.Driver"url = "jdbc:mysql://192.168.234.1:3306/seata?useUnicode=true&characterEncoding=utf8&useSSL=false&&serverTimezone=UTC" //修改數據庫連接user = "seata" //修改數據庫賬號password = "123456" //修改數據庫密碼minConn = 5maxConn = 30globalTable = "global_table"branchTable = "branch_table"lockTable = "lock_table"queryLimit = 100}
}service {vgroup_mapping.chuanzh_tx_group = "default" //chuanzh_tx_group為自定義的事務組名稱,要和客戶端配置保持一致default.grouplist = "192.168.234.128:8091"enableDegrade = falsedisable = falsemax.commit.retry.timeout = "-1"max.rollback.retry.timeout = "-1"
}

上面配置共修改了3個地方:

  1. 存儲模式改為db模式,需要創建3張事務表,如下:

    -- the table to store GlobalSession dataCREATE TABLE IF NOT EXISTS `global_table`(`xid`                       VARCHAR(128) NOT NULL,`transaction_id`            BIGINT,`status`                    TINYINT      NOT NULL,`application_id`            VARCHAR(32),`transaction_service_group` VARCHAR(32),`transaction_name`          VARCHAR(128),`timeout`                   INT,`begin_time`                BIGINT,`application_data`          VARCHAR(2000),`gmt_create`                DATETIME,`gmt_modified`              DATETIME,PRIMARY KEY (`xid`),KEY `idx_gmt_modified_status` (`gmt_modified`, `status`),KEY `idx_transaction_id` (`transaction_id`)) ENGINE = InnoDBDEFAULT CHARSET = utf8;-- the table to store BranchSession dataCREATE TABLE IF NOT EXISTS `branch_table`(`branch_id`         BIGINT       NOT NULL,`xid`               VARCHAR(128) NOT NULL,`transaction_id`    BIGINT,`resource_group_id` VARCHAR(32),`resource_id`       VARCHAR(256),`branch_type`       VARCHAR(8),`status`            TINYINT,`client_id`         VARCHAR(64),`application_data`  VARCHAR(2000),`gmt_create`        DATETIME(6),`gmt_modified`      DATETIME(6),PRIMARY KEY (`branch_id`),KEY `idx_xid` (`xid`)) ENGINE = InnoDBDEFAULT CHARSET = utf8;-- the table to store lock dataCREATE TABLE IF NOT EXISTS `lock_table`(`row_key`        VARCHAR(128) NOT NULL,`xid`            VARCHAR(96),`transaction_id` BIGINT,`branch_id`      BIGINT       NOT NULL,`resource_id`    VARCHAR(256),`table_name`     VARCHAR(32),`pk`             VARCHAR(36),`gmt_create`     DATETIME,`gmt_modified`   DATETIME,PRIMARY KEY (`row_key`),KEY `idx_branch_id` (`branch_id`)) ENGINE = InnoDBDEFAULT CHARSET = utf8;
    
  2. 修改數據庫連接,注意如果你安裝的是MySQL8,則需要修改MySQL8的驅動:driverClassName = “com.mysql.cj.jdbc.Driver”,不然會出現啟動報錯的問題,詳細請參考:seata啟動MySQL報錯 #359。

  3. 修改事務的組名,你也可以不修改,我這里使用的是:chuanzh_tx_group

  4. 創建業務事務表,記錄業務需要回滾的數據,在分布式事務中,每個參與的業務數據庫都需要添加對應的表

    CREATE TABLE `undo_log` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`branch_id` bigint(20) NOT NULL,`xid` varchar(100) NOT NULL,`context` varchar(128) NOT NULL,`rollback_info` longblob NOT NULL,`log_status` int(11) NOT NULL,`log_created` datetime NOT NULL,`log_modified` datetime NOT NULL,`ext` varchar(100) DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
    

配置conf/registry.conf文件

registry {type = "eureka"  修改注冊方式,微服務調用使用的是Eurekanacos {serverAddr = "localhost"namespace = ""cluster = "default"}eureka {serviceUrl = "http://192.168.234.1:8081/eureka"  //修改Eureka地址application = "default"  weight = "1"}redis {serverAddr = "localhost:6379"db = "0"}zk {cluster = "default"serverAddr = "127.0.0.1:2181"session.timeout = 6000connect.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"}
}

以上修改了使用Eureka方式注冊,并配置了Eureka地址,啟動MySQL、Eureka服務后,就可以啟動seata服務了。

調用方配置(store-server)

maven配置,使用seata-spring-boot-starter,自動配置的方式,不需要再添加file.conf和register.conf文件

    <dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>${druid-spring-boot-starter.version}</version></dependency><dependency><groupId>io.seata</groupId><artifactId>seata-spring-boot-starter</artifactId><version>1.2.0</version></dependency>

application.properties配置:

server.port=9090
spring.application.name=store-servermybatis.type-aliases-package=com.chuanzh.model
mybatis.mapper-locations=classpath:mapper/*.xmlspring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driverseata.tx-service-group=chuanzh_tx_group
seata.service.vgroup-mapping.chuanzh_tx_group=default
seata.service.grouplist.default=192.168.234.128:8091logging.level.io.seata=DEBUGeureka.client.serviceUrl.defaultZone= http://localhost:8081/eureka/

數據源配置,因為seata是對數據庫的datasource進行了接管和代理,所以在每個參與分布式事務的數據源都要進行如下配置:

@Configuration
public class DataSourceConfiguration {@Bean@ConfigurationProperties(prefix = "spring.datasource")public DataSource druidDataSource(){DruidDataSource druidDataSource = new DruidDataSource();return druidDataSource;}@Primary@Bean("dataSource")public DataSourceProxy dataSource(DataSource druidDataSource){return new DataSourceProxy(druidDataSource);}@Beanpublic SqlSessionFactory sqlSessionFactory(DataSourceProxy dataSourceProxy)throws Exception{SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dataSourceProxy);sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:/mapper/*.xml"));sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());return sqlSessionFactoryBean.getObject();}}

注意配置了數據源后,還需要在啟動類排除dataSource自動配置,不然會出現循環依賴的問題,如下,其它的解決方法,可以參考:集成fescar數據源循環依賴錯誤解決方案

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

配置請求攔截器,生成一個請求事務ID,用于在微服務中傳遞

@Configuration
public class SeataRequestInterceptor implements RequestInterceptor {@Overridepublic void apply(RequestTemplate requestTemplate) {String xid = RootContext.getXID();if (StringUtils.isNotBlank(xid)) {requestTemplate.header("TX_XID", xid);}}
}

服務提供方配置(company-server)

maven、application.properties、數據源配置同調用方配置,區別主要是攔截器的配置,如下:

@Slf4j
@Component
public class SeataHandlerInterceptor implements HandlerInterceptor {public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {String xid = RootContext.getXID();String rpcXid = request.getHeader("TX_XID");if(log.isDebugEnabled()) {log.debug("xid in RootContext {} xid in RpcContext {}", xid, rpcXid);}if(xid == null && rpcXid != null) {RootContext.bind(rpcXid);if(log.isDebugEnabled()) {log.debug("bind {} to RootContext", rpcXid);}}return true;}public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) {String rpcXid = request.getHeader("TX_XID");if(!StringUtils.isEmpty(rpcXid)) {String unbindXid = RootContext.unbind();if(log.isDebugEnabled()) {log.debug("unbind {} from RootContext", unbindXid);}if(!rpcXid.equalsIgnoreCase(unbindXid)) {log.warn("xid in change during RPC from {} to {}", rpcXid, unbindXid);if(unbindXid != null) {RootContext.bind(unbindXid);log.warn("bind {} back to RootContext", unbindXid);}}}}}
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {@Autowiredprivate SeataHandlerInterceptor seataHandlerInterceptor;public void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(seataHandlerInterceptor).addPathPatterns(new String[]{"/**"});}}

添加全局事務注解

在服務調用方的方法上添加@GlobalTransactional注解,下面模擬了一種場景,如果companyId為偶數,則會拋異常。

    @GlobalTransactional(rollbackFor = Exception.class)public void create(StoreEntity storeEntity) throws Exception {CompanyEntity companyEntity = new CompanyEntity();companyEntity.setName(storeEntity.getName());companyEntity = companyFeign.createCompany(companyEntity);if (companyEntity.getId() % 2 == 0) {throw new Exception();}storeEntity.setCompanyId(companyEntity.getId());storeMapper.insert(storeEntity);}

經過測試,companyFeign.createCompany服務調用后會先向數據庫寫一條數據,當create方法執行拋異常,就會事務回滾,刪除掉原先的company數據

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/167769.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/167769.shtml
英文地址,請注明出處:http://en.pswp.cn/news/167769.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

測試數據不會造?可以用這個工具Faker

在測試過程中&#xff0c;大家應該都遇到過各種各樣的數據構造問題。e.g. 構造一批通訊錄、構造一批用戶三要素(姓名手機號身份證)、構造一批銀行卡數據…… 這時候&#xff0c;測試數據大多數可能是這樣的: 張三, 130 0000 0001 李四, 130 0000 0002 王五, 130 0000 0003 …

【C/PTA —— 11.函數2(課外實踐)】

C/PTA —— 11.函數2&#xff08;課外實踐&#xff09; 一.函數題6-1 計算A[n]1/(1 A[n-1])6-2 遞歸實現順序輸出整數6-3 自然數的位數(遞歸版)6-4 分治法求解金塊問題6-5 漢諾塔6-6 重復顯示字符(遞歸版)6-7 顯示平行四邊形(右)(遞歸版) 二.編程題7-2 N階樓梯上樓問題 一.函數…

python實現存款日利息計算器(窗口界面形式)

輸入存款金額&#xff0c;7日年化收益率&#xff0c;輸出每日利息 完整源碼如下&#xff1a; import tkinter as tk from tkinter import messageboxdef calculate_interest():deposit float(entry_deposit.get())interest_rate float(entry_interest_rate.get())daily_int…

【數據結構】二叉排序樹(c風格、結合c++引用)

目錄 1 基本概念 結構體定義 各種接口 2 二叉排序樹的構建和中序遍歷 遞歸版單次插入 非遞歸版單次插入 3 二叉排序樹的查找 非遞歸版本 遞歸版本 4 二叉排序樹的刪除&#xff08;難點&#xff09; 1 基本概念 普通二叉排序樹是一種簡單的數據結構&#xff0c;節點的值…

戲說二十三種設計模式_用故事的方式就是讓你一定能懂

創建型模式 1、FACTORY—追MM少不了請吃飯了&#xff0c;麥當勞的雞翅和肯德基的雞翅都是MM愛吃的東西&#xff0c;雖然口味有所不同&#xff0c;但不管你帶MM去麥當勞或肯德基&#xff0c;只管向服務員說“來四個雞翅”就行了。麥當勞和肯德基就是生產雞翅的Factory 工廠模式&…

Cortex-M與RISC-V區別

環境 Cortex-M以STM32H750為代表&#xff0c;RISC-V以芯來為代表 RTOS版本為RT-Thread 4.1.1 寄存器 RISC-V 常用匯編 RISC-V 關于STORE x4, 4(sp)這種寄存器前面帶數字的寫法&#xff0c;其意思為將x4的值存入sp4這個地址&#xff0c;即前面的數字表示偏移的意思 反之LOA…

【LM358AD運放方波振蕩器可控輸出幅值】2022-2-25

緣由仿真如何縮小方波振蕩電路方波幅值?-有問必答-CSDN問答

使用Pytorch從零開始構建LSTM

長短期記憶&#xff08;LSTM&#xff09;網絡已被廣泛用于解決各種順序任務。讓我們了解這些網絡如何工作以及如何實施它們。 就像我們一樣&#xff0c;循環神經網絡&#xff08;RNN&#xff09;也可能很健忘。這種與短期記憶的斗爭導致 RNN 在大多數任務中失去有效性。不過&a…

發送一個網絡數據包的過程解析

在 ip_queue_xmit 中&#xff0c;也即 IP 層的發送函數里面&#xff0c;有三部分邏輯。第一部分&#xff0c;選取路由&#xff0c;也即我要發送這個包應該從哪個網卡出去。 這件事情主要由 ip_route_output_ports 函數完成。接下來的調用鏈為&#xff1a;ip_route_output_port…

改進YOLOv8 | YOLOv5系列:RFAConv續作,即插即用具有任意采樣形狀和任意數目參數的卷積核AKCOnv

RFAConv續作,構建具有任意采樣形狀的卷積AKConv 一、論文yolov5加入的方式論文 源代碼 一、論文 基于卷積運算的神經網絡在深度學習領域取得了顯著的成果,但標準卷積運算存在兩個固有缺陷:一方面,卷積運算被限制在一個局部窗口,不能從其他位置捕獲信息,并且其采樣形狀是…

Matlab進階繪圖第33期—雙曲面圖

在《Matlab論文插圖繪制模板第56期—曲面圖&#xff08;Surf&#xff09;》中&#xff0c;我分享過曲面圖的繪制模板。 然而&#xff0c;有的時候&#xff0c;需要在一張圖上繪制兩個及以上的曲面圖&#xff0c;且每個曲面圖使用不同的配色方案。 在Matlab中&#xff0c;一張…

C++基礎入門(超詳細)

話不多說&#xff0c;序言搞起來&#xff1a; 自從開始學老師布置的任務后&#xff0c;目前還是OpenCV&#xff0c;哈~哈。我就莫名問老師&#xff1a;“以后編程是用C還是python&#xff1f;”&#xff0c;果然還是太年輕&#xff0c;老師說&#xff1a;“兩們都要精通”。唉&…

set和map + multiset和multimap(使用+封裝(RBTree))

set和map 前言一、使用1. set(1)、模板參數列表(2)、常見構造(3)、find和count(4)、insert和erase(5)、iterator(6)、lower_bound和upper_bound 2. multiset3. map(1)、模板參數列表(2)、構造(3)、modifiers和operations(4)、operator[] 4. multimap 二、封裝RBTree迭代器原理R…

9.輸出國際象棋盤【2023.11.24】

1.問題描述 要求輸出國際象棋棋盤。 2.解決思路 國際象棋棋盤由64個黑白相間的格子組成&#xff0c;分為8行*8列。用i控制行&#xff0c;j控制列&#xff0c;根據ij的和的變化來控制輸出黑方格還是白方格。 3.代碼實現 #include<stdio.h> int main(){for(int i0;i&…

各操作系統之間的關系

請移步知乎&#xff1a; 操作系統UNIX、WINDOWS、LINUX、MC OS的聯系與區別 - 知乎 (zhihu.com) 移動端的android操作系統就人盡皆知啦&#xff0c;基于linux內核。 完畢。 適用領域&#xff1a; windows,macos:主要面向個人計算機市場 Linux、Windows Server:隨著互聯網的…

基于廣義正態分布算法優化概率神經網絡PNN的分類預測 - 附代碼

基于廣義正態分布算法優化概率神經網絡PNN的分類預測 - 附代碼 文章目錄 基于廣義正態分布算法優化概率神經網絡PNN的分類預測 - 附代碼1.PNN網絡概述2.變壓器故障診街系統相關背景2.1 模型建立 3.基于廣義正態分布優化的PNN網絡5.測試結果6.參考文獻7.Matlab代碼 摘要&#xf…

網絡安全—自學

1.網絡安全是什么 網絡安全可以基于攻擊和防御視角來分類&#xff0c;我們經常聽到的 “紅隊”、“滲透測試” 等就是研究攻擊技術&#xff0c;而“藍隊”、“安全運營”、“安全運維”則研究防御技術。 2.網絡安全市場 一、是市場需求量高&#xff1b; 二、則是發展相對成熟…

深度學習之基于Pytorch照片圖像轉漫畫風格網絡系統

歡迎大家點贊、收藏、關注、評論啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代碼。 文章目錄 一項目簡介 二、功能三、系統四. 總結 一項目簡介 以下是一個基本的設計介紹&#xff1a; 數據準備&#xff1a;收集足夠的真實照片和漫畫圖像&#xff0c;用于訓練模…

typora中的快捷鍵shift enter 和 enter的交換

1 問題&#xff1a; 我最近在用 typora 進行寫作&#xff0c;但是在合格 typora 的 markdown 編輯器很奇怪&#xff0c;它的一個回車符是兩次換行&#xff0c;而用 shfit ent 找了半天都不知道怎么解決的這個問題&#xff0c;然后我就去了這個 typora 在 github 開源的問題倉庫…

hive 報錯return code 40000 from org.apache.hadoop.hive.ql.exec.MoveTask解決思路

參考學習 https://github.com/apache/hive/blob/2b57dd27ad61e552f93817ac69313066af6562d9/ql/src/java/org/apache/hadoop/hive/ql/ErrorMsg.java#L47 為啥學習error code 開發過程中遇到以下錯誤&#xff0c;大家覺得應該怎么辦&#xff1f;從哪方面入手呢&#xff1f; 1.百…