Java分布式事務Seata安裝和使用,SpringCloud Seata分布式事務安裝配置和使用


Java分布式事務Seata安裝和使用,SpringCloud Seata分布式事務安裝配置和使用

 

================================

©Copyright 蕃薯耀 2021-05-10

https://www.cnblogs.com/fanshuyao/

 

一、Seata 是什么?
官方文檔:

https://seata.io/zh-cn/docs/overview/what-is-seata.html

 

Seata 是一款開源的分布式事務解決方案,致力於提供高性能和簡單易用的分布式事務服務。Seata 將為用戶提供了 AT、TCC、SAGA 和 XA 事務模式,為用戶打造一站式的分布式解決方案。


AT 模式基於 支持本地 ACID 事務 的 關系型數據庫:
一階段 prepare 行為:在本地事務中,一並提交業務數據更新和相應回滾日志記錄。
二階段 commit 行為:馬上成功結束,自動 異步批量清理回滾日志。
二階段 rollback 行為:通過回滾日志,自動 生成補償操作,完成數據回滾。

TCC 模式,不依賴於底層數據資源的事務支持:
一階段 prepare 行為:調用 自定義 的 prepare 邏輯。
二階段 commit 行為:調用 自定義 的 commit 邏輯。
二階段 rollback 行為:調用 自定義 的 rollback 邏輯。
所謂 TCC 模式,是指支持把 自定義 的分支事務納入到全局事務的管理中。

 

Seata組成:

TM (Transaction Manager) - 事務管理器
定義全局事務的范圍:開始全局事務、提交或回滾全局事務。


TC (Transaction Coordinator) - 事務協調者
維護全局和分支事務的狀態,驅動全局事務提交或回滾。

 

RM (Resource Manager) - 資源管理器
管理分支事務處理的資源,與TC交談以注冊分支事務和報告分支事務的狀態,並驅動分支事務提交或回滾。

 

TM 要求 TC 開始新的全局事務。 TC 生成一個代表全局事務的 XID。 XID 通過微服務的調用鏈傳播。

RM將本地事務作為XID對應的全局事務的一個分支注冊到TC。(RM負責注冊本地事務,根據XID將本地事務作為一個分支事務注冊到全局事務TC)

TM請求TC提交或回滾XID對應的全局事務。TC驅動XID對應的全局事務下的所有分支事務完成分支提交或回滾。


官方部署文檔:http://seata.io/zh-cn/docs/ops/deploy-guide-beginner.html

 


二、Seata Server端安裝和配置


基於seata-server-1.4.2.zip實現,同時也要下載源碼包seata-1.4.2.zip(DB模式下要執行SQL腳本以及Nacos作為配置中心,Seata相關的配置需要注冊到Nacos)

下載地址:

https://github.com/seata/seata/releases

 

Server端存儲模式(store.mode)現有file、db、redis三種(后續將引入raft,mongodb),file模式無需改動,直接啟動即可,下面專門講下db啟動步驟。
注:
file模式為單機模式,全局事務會話信息內存中讀寫並持久化本地文件root.data,性能較高;
db模式為高可用模式,全局事務會話信息通過db共享,相應性能差些;
redis模式Seata-Server 1.3及以上版本支持,性能較高,存在事務信息丟失風險,請提前配置合適當前場景的redis持久化配置.


1、解壓:seata-server-1.4.2.zip

 

2、進入seata-server目錄,修改配置文件

目錄是:

seata-server-1.4.2\conf

 

需要修改的配置文件:
registry.conf
file.conf

registry.conf指定注冊模式,如 # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa,默認:type = "file",指向file.conf文件
file.conf:有模式:file、db、redis,用於存儲事務的日記。

registry.conf當前指定使用nacos作為注冊中心和配置中心,需要修改registry項和config項,同時修改nacos的連接信息。
建議增加namespace的配置,將Seata相應的配置放在一起,不然后面配置中心的東西很多,有整整9頁

 

有命名空間的配置,即修改namespace:

registry {
  type = "nacos"

  nacos {
    application = "seata-server"
    serverAddr = "127.0.0.1:8848"
    group = "SEATA_GROUP"
    namespace = "fb31c4c2-29ac-456c-b9d6-36301baceae4"
    cluster = "default"
    username = ""
    password = ""
  }
}

config {
  type = "nacos"

  nacos {
    serverAddr = "127.0.0.1:8848"
    namespace = "fb31c4c2-29ac-456c-b9d6-36301baceae4"
    group = "SEATA_GROUP"
    username = ""
    password = ""
    dataId = "seataServer.properties"
  }
}

 

file.conf修改內容如下:(需要預先創建一個seata的數據庫,用於事務的日志記錄)

## transaction log store, only used in seata-server
store {
  mode = "db"
  publicKey = ""
  db {
    datasource = "druid"
    dbType = "mysql"
    driverClassName = "com.mysql.jdbc.Driver"
    url = "jdbc:mysql://127.0.0.1:3307/seata?rewriteBatchedStatements=true"
    user = "root"
    password = "root"
    minConn = 5
    maxConn = 100
    globalTable = "global_table"
    branchTable = "branch_table"
    lockTable = "lock_table"
    queryLimit = 100
    maxWait = 5000
  }
}

 

3、執行seata server端數據庫腳本

Server端:server端數據庫腳本 (包含 lock_table、branch_table 與 global_table) 及各個容器配置,腳本需要在源碼包找(seata-1.4.2.zip)
執行腳本(僅使用DB模式才需要執行腳本)
腳本所在的目錄(seata-1.4.2為源碼seata-1.4.2.zip):

seata-1.4.2\script\server\db

 

總共有3個文件(只需要執行自己對應的數據腳本即可):
mysql.sql
oracle.sql
postgresql.sql


下面為mysql.sql

-- -------------------------------- The script used when storeMode is 'db' --------------------------------
-- the table to store GlobalSession data
CREATE 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 = InnoDB
  DEFAULT CHARSET = utf8;

-- the table to store BranchSession data
CREATE 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 = InnoDB
  DEFAULT CHARSET = utf8;

-- the table to store lock data
CREATE TABLE IF NOT EXISTS `lock_table`
(
    `row_key`        VARCHAR(128) NOT NULL,
    `xid`            VARCHAR(128),
    `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 = InnoDB
  DEFAULT CHARSET = utf8;

 

4、啟動Seata
注:啟動Seata,記得先啟動Nacos

Windows啟動:
進入目錄:seata-server-1.4.2\bin,雙擊seata-server.bat直接啟動

Linux命令啟動: 
seata-server.sh -h 127.0.0.1 -p 8091 -m db -n 1 -e test

 

源碼啟動: 執行Server.java的main方法

 

參數說明:

-h: 注冊到注冊中心的ip
-p: Server rpc 監聽端口
-m: 全局事務會話信息存儲模式,file、db、redis,優先讀取啟動參數 (Seata-Server 1.3及以上版本支持redis)
-n: Server node,多個Server時,需區分各自節點,用於生成不同區間的transactionId,以免沖突
-e: 多環境配置參考 http://seata.io/en-us/docs/ops/multi-configuration-isolation.html

 

 

啟動成功后:
16:52:56.108 INFO --- [ main] io.seata.config.FileConfiguration : The file name of the operation is file.conf
16:52:56.108 INFO --- [ main] io.seata.config.FileConfiguration : The configuration file used is D:\0soft\seata-server-1.4.2\conf\file.conf
16:52:57.904 INFO --- [ main] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} inited
16:52:58.450 INFO --- [ main] i.s.core.rpc.netty.NettyServerBootstrap : Server started, listen port: 8091

 


5、導入Seata相應的配置項到Nacos的配置中心(非注冊中心)
這點很容易漏。
config-center 官網說明地址是:

https://github.com/seata/seata/tree/develop/script/config-center

 

該文件是:config.txt

注意:在seata-server-1.4.2是沒有config.txt文件的,該文件在源碼包中:seata-1.4.2.zip,解壓后的目錄是:

seata-1.4.2\script\config-center\config.txt

 

(可以將seata-1.4.2\script\config-center復制一份到seata-server-1.4.2\config-center)

當前使用的是Nacos作為配置中心(config-center),所以進入到nacos目錄:

seata-1.4.2\script\config-center\nacos 

 

有兩個腳本文件:
nacos-config.py
nacos-config.sh

第一個是phython腳本,第二個是Linux腳本
那Windows腳本怎么執行呢?安裝Git-2.26.2-64-bit.exe,使用Git Bash執行。(省略具體步驟)

執行前,需要修改config.txt文件:

my_test_tx_group是默認有的,沒有修改。增加分布式事務分組nacos_service_tx_group、nacos_consumer_tx_group。注意:名稱長度不能超過VARCHAR(32)。事務分組允許只配置一個,也可以配置成多個,用於切換,此處額外增加2個,具體配置如下:

service.vgroupMapping.my_test_tx_group=default
service.vgroupMapping.nacos_service_tx_group=default
service.vgroupMapping.nacos_consumer_tx_group=default

 

全局事務表global_table的transaction_service_group長度為32(VARCHAR(32)),超出會報錯。例如:SPRING-CLOUD-NACOS-CONSUMER_tx_group長度為36,就會導致報錯。

service.vgroupMapping.SPRING-CLOUD-NACOS-CONSUMER_tx_group=default:命名超出數據庫的長度會報錯。

Caused by: io.seata.core.exception.TmTransactionException: TransactionException[begin global request failed. xid=null, msg=Data truncation: Data too long for column 'transaction_service_group' at row 1]
at io.seata.tm.DefaultTransactionManager.begin(DefaultTransactionManager.java:55)
at io.seata.tm.api.DefaultGlobalTransaction.begin(DefaultGlobalTransaction.java:105)
at io.seata.tm.api.TransactionalTemplate.beginTransaction(TransactionalTemplate.java:215)

 

什么是事務分組?見:

https://seata.io/zh-cn/docs/user/txgroup/transaction-group.html

 


修改數據存儲模式為db模式,修改數據連接和賬號密碼

store.mode=db
store.db.url=jdbc:mysql://127.0.0.1:3307/seata?useUnicode=true&rewriteBatchedStatements=true
store.db.user=root
store.db.password=root

 


config.txt修改完成后,通過Git Bash將相應的配置導致到Nacos配置中心,命令如下(路徑必須是/,不能是\):

sh D:/0soft/seata-server-1.4.2/config-center/nacos/nacos-config.sh -h 127.0.0.1 -p 8848 -g SEATA_GROUP

 

建議增加namespace的配置(-t參數,命名空間的id是在Nacos創建生成的),將Seata相應的配置放在一起,不然后面配置中心的東西很多,有整整9頁,而且屬性很多,很有可能和其它屬性出現重復沖突

sh D:/0soft/seata-server-1.4.2/config-center/nacos/nacos-config.sh -h 127.0.0.1 -p 8848 -g SEATA_GROUP -t fb31c4c2-29ac-456c-b9d6-36301baceae4

 

如果Nacos設置和賬號和密碼,也可以設置

sh D:/0soft/seata-server-1.4.2/config-center/nacos/nacos-config.sh -h 127.0.0.1 -p 8848 -g SEATA_GROUP -t fb31c4c2-29ac-456c-b9d6-36301baceae4 -u username -w password

參數說明:

-h:主機,默認值為localhost。
-p:端口,默認值為8848。
-g:配置分組,默認值為“ SEATA_GROUP”。
-t:租戶信息,對應於Nacos的名稱空間ID字段,默認值為空。
-u:用戶名,權限控制在的nacos 1.2.0+,默認值為空。
-w:密碼,權限控制在的nacos 1.2.0+,默認值為空。

 


config.txt文件腳本執行后,成功91,失敗4個,但失敗的不影響Seata運行。

=========================================================================
 Complete initialization parameters,  total-count:91 ,  failure-count:4
=========================================================================
 init nacos config fail.

失敗的4個是:

Set store.publicKey= failure
Set store.redis.sentinel.masterName= failure
Set store.redis.sentinel.sentinelHosts= failure
Set store.redis.password= failure

 


不導入config.txt文件,項目加入Seata依賴和配置啟動后,會出現下面的錯誤:can not get cluster name in registry config,詳細如下:

SPRING-CLOUD-NACOS-SERVICE服務提供者:

2021-05-10 09:27:18.719 ERROR 13544 --- [eoutChecker_1_1] i.s.c.r.netty.NettyClientChannelManager : can not get cluster name in registry config 'service.vgroupMapping.SPRING-CLOUD-NACOS-SERVICE_tx_group', please make sure registry config correct
2021-05-10 09:27:18.753 ERROR 13544 --- [eoutChecker_2_1] i.s.c.r.netty.NettyClientChannelManager : can not get cluster name in registry config 'service.vgroupMapping.SPRING-CLOUD-NACOS-SERVICE_tx_group', please make sure registry config correct

 

SPRING-CLOUD-NACOS-CONSUMER服務消費者:

2021-05-10 09:27:33.786 ERROR [SPRING-CLOUD-NACOS-CONSUMER,,,] 14916 --- [eoutChecker_1_1] i.s.c.r.netty.NettyClientChannelManager : can not get cluster name in registry config 'service.vgroupMapping.SPRING-CLOUD-NACOS-CONSUMER_tx_group', please make sure registry config correct
2021-05-10 09:27:33.820 ERROR [SPRING-CLOUD-NACOS-CONSUMER,,,] 14916 --- [eoutChecker_2_1] i.s.c.r.netty.NettyClientChannelManager : can not get cluster name in registry config 'service.vgroupMapping.SPRING-CLOUD-NACOS-CONSUMER_tx_group', please make sure registry config correct

 


如果沒有什么問題,項目啟動后的信息則是register TM success,register success,:

2021-05-10 10:12:20.315 INFO 2120 --- [eoutChecker_1_1] i.s.c.r.netty.NettyClientChannelManager : will connect to 192.168.170.1:8091
2021-05-10 10:12:20.319 INFO 2120 --- [eoutChecker_1_1] i.s.core.rpc.netty.NettyPoolableFactory : NettyPool create channel to transactionRole:TMROLE,address:192.168.170.1:8091,msg:< RegisterTMRequest{applicationId='SPRING-CLOUD-NACOS-SERVICE', transactionServiceGroup='nacos_service_tx_group'} >
2021-05-10 10:12:20.338 INFO 2120 --- [eoutChecker_1_1] i.s.c.rpc.netty.TmNettyRemotingClient : register TM success. client version:1.4.2, server version:1.4.2,channel:[id: 0x0e398621, L:/192.168.170.1:55953 - R:/192.168.170.1:8091]
2021-05-10 10:12:20.338 INFO 2120 --- [eoutChecker_1_1] i.s.core.rpc.netty.NettyPoolableFactory : register success, cost 7 ms, version:1.4.2,role:TMROLE,channel:[id: 0x0e398621, L:/192.168.170.1:55953 - R:/192.168.170.1:8091]

2021-05-10 10:12:35.467 INFO [SPRING-CLOUD-NACOS-CONSUMER,,,] 13144 --- [eoutChecker_1_1] i.s.c.r.netty.NettyClientChannelManager : will connect to 192.168.170.1:8091 2021-05-10 10:12:35.468 INFO [SPRING-CLOUD-NACOS-CONSUMER,,,] 13144 --- [eoutChecker_1_1] i.s.core.rpc.netty.NettyPoolableFactory : NettyPool create channel to transactionRole:TMROLE,address:192.168.170.1:8091,msg:< RegisterTMRequest{applicationId='SPRING-CLOUD-NACOS-CONSUMER', transactionServiceGroup='nacos_consumer_tx_group'} > 2021-05-10 10:12:35.474 INFO [SPRING-CLOUD-NACOS-CONSUMER,,,] 13144 --- [eoutChecker_1_1] i.s.c.rpc.netty.TmNettyRemotingClient : register TM success. client version:1.4.2, server version:1.4.2,channel:[id: 0xe9bfdcf1, L:/192.168.170.1:55954 - R:/192.168.170.1:8091] 2021-05-10 10:12:35.474 INFO [SPRING-CLOUD-NACOS-CONSUMER,,,] 13144 --- [eoutChecker_1_1] i.s.core.rpc.netty.NettyPoolableFactory : register success, cost 4 ms, version:1.4.2,role:TMROLE,channel:[id: 0xe9bfdcf1, L:/192.168.170.1:55954 - R:/192.168.170.1:8091]

 


Seata事務回滾:Rollback global transaction successfully,

10:13:07.415 INFO --- [rverHandlerThread_1_3_500] i.s.s.coordinator.DefaultCoordinator : Begin new global transaction applicationId: SPRING-CLOUD-NACOS-CONSUMER,transactionServiceGroup: nacos_consumer_tx_group, transactionName: saveClazzAndStudentError(),timeout:60000,xid:192.168.170.1:8091:4936076990459990017
10:13:14.398 INFO --- [ batchLoggerPrint_1_1] i.s.c.r.p.server.BatchLogHandler : xid=192.168.170.1:8091:4936076990459990017,extraData=null,clientIp:192.168.170.1,vgroup:nacos_consumer_tx_group
10:13:14.873 INFO --- [rverHandlerThread_1_4_500] io.seata.server.coordinator.DefaultCore : Rollback global transaction successfully, xid = 192.168.170.1:8091:4936076990459990017.

 

 

 


三、Seata Client 端安裝和配置

1、項目說明
客戶端有2個項目:
springCloud-8802-nacos-provider:服務提供者
springCloud-8805-nacos-web:服務消費者

springCloud-8805-nacos-web會去調用springCloud-8802-nacos-provider,用於測試分布式事務。
springCloud-8802-nacos-provider連接數據庫:seata-biz-1
springCloud-8805-nacos-web連接數據庫:seata-biz-2


2、Seata Client 端的兩個數據庫分別執行回滾日志的Sql腳本:undo_log。只需要在客戶端的數據庫添加表(注意:每個客戶端的數據庫都要增加,此處為:seata-biz-1和seata-biz-2),Seata服務端的數據是不需要的

腳本在源碼包,目錄是:

seata-1.4.2\script\client\at\db

 

mysql.sql腳本如下:

-- for AT mode you must to init this sql for you business database. the seata server not need it.
CREATE TABLE IF NOT EXISTS `undo_log`
(
    `branch_id`     BIGINT       NOT NULL COMMENT 'branch transaction id',
    `xid`           VARCHAR(128) NOT NULL COMMENT 'global transaction id',
    `context`       VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
    `rollback_info` LONGBLOB     NOT NULL COMMENT 'rollback info',
    `log_status`    INT(11)      NOT NULL COMMENT '0:normal status,1:defense status',
    `log_created`   DATETIME(6)  NOT NULL COMMENT 'create datetime',
    `log_modified`  DATETIME(6)  NOT NULL COMMENT 'modify datetime',
    UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB
  AUTO_INCREMENT = 1
  DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';

 

3、項目添加seata依賴

Seata依賴說明(建議單選)
依賴seata-all
依賴seata-spring-boot-starter,支持yml、properties配置(.conf可刪除),內部已依賴seata-all
依賴spring-cloud-alibaba-seata,內部集成了seata,並實現了xid傳遞

以spring-cloud-alibaba-seata為例:

<properties>
    <seata.version>1.4.2</seata.version>
</properties>

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

<!-- <version>2.2.5.RELEASE</version> -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    <exclusions>
    <exclusion>
        <groupId>io.seata</groupId>
        <artifactId>seata-spring-boot-starter</artifactId>
    </exclusion>
    </exclusions>
</dependency>

 

 

4、application.properties增加Seata的配置

springCloud-8802-nacos-provider項目:

配置:seata.tx-service-group,和Git 執行腳本生成的Nacos配置的事務分組要保持一致。

配置:seata.config.nacos.namespace,服務注冊的命名空間。

 

 

#更多配置項見:https://seata.io/zh-cn/docs/user/configurations.html
#是否開啟spring-boot自動裝配。truefalse,(SSBS)專有配置,默認true
#seata.enabled=true
#是否開啟數據源自動代理。    truefalse,seata-spring-boot-starter(SSBS)專有配置,SSBS默認會開啟數據源自動代理,可通過該配置項關閉.
#seata.enableAutoDataSourceProxy=true
#是否使用JDK代理作為數據源自動代理的實現方式。truefalse,(SSBS)專有配置,默認false,采用CGLIB作為數據源自動代理的實
#seata.useJdkProxy=false
#一階段全局提交結果上報TC重試次數,默認1次,建議大於1
seata.client.tm.commitRetryCount=3
#一階段全局回滾結果上報TC重試次數。    默認1次,建議大於1
seata.client.tm.rollbackRetryCount=3
#undo序列化方式    默認jackson
#seata.client.undo.logSerialization
#自定義undo表名    默認undo_log
#seata.client.undo.logTable

#類文件:io.seata.spring.boot.autoconfigure.properties.SeataProperties
seata.application-id=${spring.application.name}
seata.tx-service-group=nacos_service_tx_group

seata.config.type=nacos
seata.config.nacos.namespace=fb31c4c2-29ac-456c-b9d6-36301baceae4
seata.config.nacos.server-addr=127.0.0.1:8848
seata.config.nacos.group=SEATA_GROUP
#seata.config.nacos.username=
#seata.config.nacos.password=

seata.registry.type=nacos
seata.registry.nacos.application=seata-server
seata.registry.nacos.server-addr=127.0.0.1:8848
seata.registry.nacos.group=SEATA_GROUP
seata.registry.nacos.namespace=fb31c4c2-29ac-456c-b9d6-36301baceae4
#seata.registry.nacos.username=
#seata.registry.nacos.password=

 

springCloud-8805-nacos-web項目:

注意配置項:seata.tx-service-group=nacos_consumer_tx_group

#更多配置項見:https://seata.io/zh-cn/docs/user/configurations.html
#是否開啟spring-boot自動裝配。truefalse,(SSBS)專有配置,默認true
#seata.enabled=true
#是否開啟數據源自動代理。    truefalse,seata-spring-boot-starter(SSBS)專有配置,SSBS默認會開啟數據源自動代理,可通過該配置項關閉.
#seata.enableAutoDataSourceProxy=true
#是否使用JDK代理作為數據源自動代理的實現方式。truefalse,(SSBS)專有配置,默認false,采用CGLIB作為數據源自動代理的實
#seata.useJdkProxy=false
#一階段全局提交結果上報TC重試次數,默認1次,建議大於1
seata.client.tm.commitRetryCount=3
#一階段全局回滾結果上報TC重試次數。    默認1次,建議大於1
seata.client.tm.rollbackRetryCount=3
#undo序列化方式    默認jackson
#seata.client.undo.logSerialization
#自定義undo表名    默認undo_log
#seata.client.undo.logTable

#io.seata.spring.boot.autoconfigure.properties.SeataProperties
seata.application-id=${spring.application.name}
seata.tx-service-group=nacos_consumer_tx_group

seata.config.type=nacos
seata.config.nacos.namespace=fb31c4c2-29ac-456c-b9d6-36301baceae4
seata.config.nacos.server-addr=127.0.0.1:8848
seata.config.nacos.group=SEATA_GROUP
#seata.config.nacos.username=
#seata.config.nacos.password=

seata.registry.type=nacos
seata.registry.nacos.application=seata-server
seata.registry.nacos.server-addr=127.0.0.1:8848
seata.registry.nacos.group=SEATA_GROUP
seata.registry.nacos.namespace=fb31c4c2-29ac-456c-b9d6-36301baceae4
#seata.registry.nacos.username=
#seata.registry.nacos.password=

 

springCloud-8802-nacos-provider項目和springCloud-8805-nacos-web項目的Seata配置的區別是:seata.tx-service-group不同,即事務分組不同。

 

5、springCloud-8805-nacos-web項目的服務請求(Service)增加@GlobalTransactional全局事務。

使用seata-spring-boot-starter,Seata默認是自動配置,自動代理數據源的,所以不需要在啟動類加什么注解。只需要在方法加上@GlobalTransactional(rollbackFor = Throwable.class),而且不能加@Transactional(這個注解會導致Seata表的undo_log無數據,但Seata事務能正常提交和回滾)

@Transactional
    @Override
    public Clazz save() {
        Clazz clazz = new Clazz();
        clazz.setClazzName("班級:" + RandomUtil.randomString(5));
        
        this.baseMapper.insert(clazz);
        
        return clazz;
    }
    
    
    @Transactional
    @Override
    public void saveClassAndStudent() {
        Clazz clazz = this.save();
        Result resultStudent=  restTemplate.getForObject("http://" + myNacosServiceName + "/student/save", Result.class);
        
        log.info("clazz=" + JsonUtil.toJson(clazz));
        log.info("resultStudent=" + JsonUtil.toJson(resultStudent));
    }
    
    
    /**
     * 這里作為事務發起端,加@GlobalTransactional,啟動全局事務
     * 這里不能加@Transactional,不然受到本地事務管理,導致Seata產生的日志受到本地事務管理,在服務提供者端打斷點看不到Seata表中有數據。
     */
    @GlobalTransactional(rollbackFor = Throwable.class)
    @Override
    public void saveClazzAndStudentError() {
        //本地事務的方法
        Clazz clazz = this.save();
        
        //調用服務提供者的方法
        Result resultStudent=  restTemplate.getForObject("http://" + myNacosServiceName + "/student/saveError", Result.class);
        
        log.info("clazz=" + JsonUtil.toJson(clazz));
        log.info("resultStudent=" + JsonUtil.toJson(resultStudent));
    }

 

springCloud-8802-nacos-provider項目是不需要增加@GlobalTransactional全局事務,在事務發起端配置@GlobalTransactional即可。

//@Transactional//(使用這個注解會導致Seata表的undo_log無數據)
@Override
public Student save() {
    Student s = new Student();
    s.setStuName("學生:" + RandomUtil.randomString(5));
    s.setStuNo(UidUtil.getUid());
    
    this.baseMapper.insert(s);
    
    return s;
}


//@Transactional//(使用這個注解會導致Seata表的undo_log無數據)
@Override
public Student saveError() {
    Student s = new Student();
    s.setStuName("學生:" + RandomUtil.randomString(5));
    s.setStuNo(UidUtil.getUid());
    
    this.baseMapper.insert(s);
    
    
    int a = 0; 
    System.out.println(3/a);
    
    return s;
}

 

然后進行Seata分布式事務測試,包括成功和不成功(回滾)。


Seata分布式事務的配置和使用,到此已經完結。

 

后面的是擴展,有興趣可繼續往下看。

 

 

 

 

 

 

 

 

 

四、Seata分布式事務其它項說明


1、數據源代理(不支持自動和手動配置並存,不支持XA數據源自動代理)

0.9.0版本開始seata支持自動代理數據源

1.1.0: seata-all取消屬性配置,改由注解@EnableAutoDataSourceProxy開啟,並可選擇jdk proxy或者cglib proxy
1.0.0: client.support.spring.datasource.autoproxy=true
0.9.0: support.spring.datasource.autoproxy=true


手動配置可參考下方的例子

    @Primary
    @Bean("dataSource")
    public DataSourceProxy dataSource(DataSource druidDataSource) {
        //AT 代理 二選一
        return new DataSourceProxy(druidDataSource);
        //XA 代理
        return new DataSourceProxyXA(druidDataSource)
    }

 

2、初始化GlobalTransactionScanner


手動方式:

       @Bean
       public GlobalTransactionScanner globalTransactionScanner() {
           String applicationName = this.applicationContext.getEnvironment().getProperty("spring.application.name");
           String txServiceGroup = this.seataProperties.getTxServiceGroup();
           if (StringUtils.isEmpty(txServiceGroup)) {
               txServiceGroup = applicationName + "-fescar-service-group";
               this.seataProperties.setTxServiceGroup(txServiceGroup);
           }
   
           return new GlobalTransactionScanner(applicationName, txServiceGroup);
       }

 

自動方式:

引入seata-spring-boot-starter、spring-cloud-starter-alibaba-seata等jar

 

3、實現xid跨服務傳遞
手動 參考源碼integration文件夾下的各種rpc實現 module
自動 springCloud用戶可以引入spring-cloud-starter-alibaba-seata,內部已經實現xid傳遞

 

 

 


五、Seata事務分組
官網說明:

https://seata.io/zh-cn/docs/user/txgroup/transaction-group-and-ha.html

 

事務分組是什么?
事務分組是seata的資源邏輯,類似於服務實例。在file.conf中的my_test_tx_group就是一個事務分組。

通過事務分組如何找到后端集群?
首先程序中配置了事務分組(GlobalTransactionScanner 構造方法的txServiceGroup參數)
程序會通過用戶配置的配置中心去尋找service.vgroupMapping .[事務分組配置項],取得配置項的值就是TC集群的名稱
拿到集群名稱程序通過一定的前后綴+集群名稱去構造服務名,各配置中心的服務名實現不同
拿到服務名去相應的注冊中心去拉取相應服務名的服務列表,獲得后端真實的TC服務列表

為什么這么設計,不直接取服務名?
這里多了一層獲取事務分組到映射集群的配置。這樣設計后,事務分組可以作為資源的邏輯隔離單位,出現某集群故障時可以快速failover,只切換對應分組,可以把故障縮減到服務級別,但前提也是你有足夠server集群。

 


最佳實踐1:TC的異地多機房容災


假定TC集群部署在兩個機房:guangzhou機房(主)和shanghai機房(備)各兩個實例
一整套微服務架構項目:projectA
projectA內有微服務:serviceA、serviceB、serviceC 和 serviceD
其中,projectA所有微服務的事務分組tx-transaction-group設置為:projectA,projectA正常情況下使用guangzhou的TC集群(主)

那么正常情況下,client端的配置如下所示:
seata.tx-service-group=projectA
seata.service.vgroup-mapping.projectA=Guangzhou

假如此時guangzhou集群分組整個down掉,或者因為網絡原因projectA暫時無法與Guangzhou機房通訊,那么我們將配置中心中的Guangzhou集群分組改為Shanghai,如下:
seata.service.vgroup-mapping.projectA=Shanghai

並推送到各個微服務,便完成了對整個projectA項目的TC集群動態切換。

 

最佳實踐2:單一環境多應用接入


假設現在開發環境(或預發/生產)中存在一整套seata集群
seata集群要服務於不同的微服務架構項目projectA、projectB、projectC
projectA、projectB、projectC之間相對獨立
我們將seata集群中的六個實例兩兩分組,使其分別服務於projectA、projectB與projectC,那么此時seata-server端的配置如下(以nacos注冊中心為例)

 

最佳實踐3:client的精細化控制


假定現在存在seata集群,Guangzhou機房實例運行在性能較高的機器上,Shanghai集群運行在性能較差的機器上
現存微服務架構項目projectA、projectA中有微服務ServiceA、ServiceB、ServiceC與ServiceD
其中ServiceD的流量較小,其余微服務流量較大
那么此時,我們可以將ServiceD微服務引流到Shanghai集群中去,將高性能的服務器讓給其余流量較大的微服務(反之亦然,若存在某一個微服務流量特別大,我們也可以單獨為此微服務開辟一個更高性能的集群,並將該client的virtual group指向該集群,其最終目的都是保證在流量洪峰時服務的可用)

 

 

六、Seata配置中心


什么是配置中心?配置中心可以說是一個"大衣櫃",內部放置着各種配置文件,你可以通過自己所需進行獲取配置加載到對應的客戶端.比如Seata Client端(TM,RM),Seata Server(TC),會去讀取全局事務開關,事務會話存儲模式等信息.
Seata的配置中心與Spring cloud的配置中心區別是?在廣義上來說,並無區別,只不過Spring cloud的配置中心僅是作用於它們自身的組件,而Seata的配置中心也是一樣是作用於Seata自身.(注:Spring cloud的配置中心與Seata無關)
Seata支持哪些配置中心?
consul
apollo
etcd
zookeeper
redis
file (讀本地文件)

 

 

七、Seata注冊中心


什么是注冊中心?注冊中心可以說是微服務架構中的”通訊錄“,它記錄了服務和服務地址的映射關系。在分布式架構中,服務會注冊到這里,當服務需要調用其它服務時,就到這里找到服務的地址,進行調用.比如Seata Client端(TM,RM),發現Seata Server(TC)集群的地址,彼此通信.
Seata的注冊中心與Dubbo,Spring cloud的注冊中心區別是?在廣義上來說,並無區別,只不過Dubbo與Spring cloud的注冊中心僅是作用於它們自身的組件,而Seata的注冊中心也是一樣是作用於Seata自身.(注:Dubbo與Spring cloud的注冊中心與Seata無關)
Seata支持哪些注冊中心?
eureka
consul
apollo
etcd
zookeeper
sofa
redis
file (直連)

 

 

八、Seata微服務框架支持


1、Seata事務上下文

Seata 的事務上下文由 RootContext 來管理。
應用開啟一個全局事務后,RootContext 會自動綁定該事務的 XID,事務結束(提交或回滾完成),RootContext 會自動解綁 XID。

// 綁定 XID
RootContext.bind(xid);

// 解綁 XID
String xid = RootContext.unbind();
應用可以通過 RootContext 的 API 接口來獲取當前運行時的全局事務 XID。

// 獲取 XID
String xid = RootContext.getXID();
應用是否運行在一個全局事務的上下文中,就是通過 RootContext 是否綁定 XID 來判定的。

public static boolean inGlobalTransaction() {
return CONTEXT_HOLDER.get(KEY_XID) != null;
}

 


2、Seata事務傳播
Seata 全局事務的傳播機制就是指事務上下文的傳播,根本上,就是 XID 的應用運行時的傳播方式。

1. 服務內部的事務傳播

默認的,RootContext 的實現是基於 ThreadLocal 的,即 XID 綁定在當前線程上下文中。

所以服務內部的 XID 傳播通常是天然的通過同一個線程的調用鏈路串連起來的。默認不做任何處理,事務的上下文就是傳播下去的。

如果希望掛起事務上下文,則需要通過 RootContext 提供的 API 來實現:

// 掛起(暫停)
String xid = RootContext.unbind();

// TODO: 運行在全局事務外的業務邏輯

// 恢復全局事務上下文
RootContext.bind(xid);

 

2. 跨服務調用的事務傳播

通過上述基本原理,我們可以很容易理解:

跨服務調用場景下的事務傳播,本質上就是要把 XID 通過服務調用傳遞到服務提供方,並綁定到 RootContext 中去。

只要能做到這點,理論上 Seata 可以支持任意的微服務框架

 

九、Seata各種模式比較


1、Seata AT 模式:https://seata.io/zh-cn/docs/dev/mode/at-mode.html

前提
基於支持本地 ACID 事務的關系型數據庫。
Java 應用,通過 JDBC 訪問數據庫。

整體機制
兩階段提交協議的演變:

一階段:業務數據和回滾日志記錄在同一個本地事務中提交,釋放本地鎖和連接資源。

二階段:

提交異步化,非常快速地完成。
回滾通過一階段的回滾日志進行反向補償。

 


2、Seata TCC 模式(https://seata.io/zh-cn/docs/dev/mode/tcc-mode.html)

回顧總覽中的描述:一個分布式的全局事務,整體是 兩階段提交 的模型。全局事務是由若干分支事務組成的,分支事務要滿足 兩階段提交 的模型要求,即需要每個分支事務都具備自己的:

一階段 prepare 行為
二階段 commit 或 rollback 行為

根據兩階段行為模式的不同,我們將分支事務划分為 Automatic (Branch) Transaction Mode 和 TCC (Branch) Transaction Mode.

AT 模式(參考鏈接 TBD)基於 支持本地 ACID 事務 的 關系型數據庫:
一階段 prepare 行為:在本地事務中,一並提交業務數據更新和相應回滾日志記錄。
二階段 commit 行為:馬上成功結束,自動 異步批量清理回滾日志。
二階段 rollback 行為:通過回滾日志,自動 生成補償操作,完成數據回滾。

相應的,TCC 模式,不依賴於底層數據資源的事務支持:
一階段 prepare 行為:調用 自定義 的 prepare 邏輯。
二階段 commit 行為:調用 自定義 的 commit 邏輯。
二階段 rollback 行為:調用 自定義 的 rollback 邏輯。
所謂 TCC 模式,是指支持把 自定義 的分支事務納入到全局事務的管理中。


3、SEATA Saga 模式(https://seata.io/zh-cn/docs/user/saga.html)
概述
Saga模式是SEATA提供的長事務解決方案,在Saga模式中,業務流程中每個參與者都提交本地事務,當出現某一個參與者失敗則補償前面已經成功的參與者,一階段正向服務和二階段補償服務都由業務開發實現。

適用場景:
業務流程長、業務流程多
參與者包含其它公司或遺留系統服務,無法提供 TCC 模式要求的三個接口

優勢:
一階段提交本地事務,無鎖,高性能
事件驅動架構,參與者可異步執行,高吞吐
補償服務易於實現

缺點:
不保證隔離性(應對方案見后面文檔)


4、Seata XA 模式(https://seata.io/zh-cn/docs/dev/mode/xa-mode.html)
前提
支持XA 事務的數據庫。
Java 應用,通過 JDBC 訪問數據庫。

整體機制
在 Seata 定義的分布式事務框架內,利用事務資源(數據庫、消息服務等)對 XA 協議的支持,以 XA 協議的機制來管理分支事務的一種 事務模式。

 

 

十、Seata1.4.2問題踩坑

1、Seata branch_table無事務數據、Seata  lock_table 無事務數據、Seata  undo_log無數據、Seata  undo_log無回滾日志、seata1.4 @Transactional沖突 

在測試過程中,發現Seata  branch_table、lock_table 、undo_log無數據,只有global_table有數據,但Seata 事務能正常提交、回滾。

但為什么沒數據呢?

最開始是@GlobalTransactional和@Transactional兩個注解一起使用,雖然Seata事務能正常提交和回滾,但后面發現Seata  undo_log無數據,只有global_table有一條數據,其它表也沒有數據

    @GlobalTransactional(rollbackFor = Throwable.class)
    @Transactional
    @Override
    public void saveClazzAndStudentError() {
        Clazz clazz = this.save();
        Result resultStudent=  restTemplate.getForObject("http://" + myNacosServiceName + "/student/saveError", Result.class);
        
        log.info("clazz=" + JsonUtil.toJson(clazz));
        log.info("resultStudent=" + JsonUtil.toJson(resultStudent));
    }

 原因竟然是:seata的@GlobalTransactional和@Transactional沖突 

后面所有項目的所有方法都改成只使用@GlobalTransactional時,所有表都有數據了,而且Seata事務也能正常提交和回滾。暫時不知道是什么原因導致的。

@GlobalTransactional(rollbackFor = Throwable.class)

 

 

十一、注意事項

 官網注意事項:

http://seata.io/zh-cn/docs/overview/faq.html

 

1、使用 AT 模式需要的注意事項有哪些 ?

  1. 必須使用代理數據源,有 3 種形式可以代理數據源:
  • 依賴 seata-spring-boot-starter 時,自動代理數據源,無需額外處理。
  • 依賴 seata-all 時,使用 @EnableAutoDataSourceProxy (since 1.1.0) 注解,注解參數可選擇 jdk 代理或者 cglib 代理。
  • 依賴 seata-all 時,也可以手動使用 DatasourceProxy 來包裝 DataSource。
  1. 配置 GlobalTransactionScanner,使用 seata-all 時需要手動配置,使用 seata-spring-boot-starter 時無需額外處理。
  2. 業務表中必須包含單列主鍵,若存在復合主鍵,請參考問題 13 。
  3. 每個業務庫中必須包含 undo_log 表,若與分庫分表組件聯用,分庫不分表。
  4. 跨微服務鏈路的事務需要對相應 RPC 框架支持,目前 seata-all 中已經支持:Apache Dubbo、Alibaba Dubbo、sofa-RPC、Motan、gRpc、httpClient,對於 Spring Cloud 的支持,請大家引用 spring-cloud-alibaba-seata。其他自研框架、異步模型、消息消費事務模型請結合 API 自行支持。
  5. 目前AT模式支持的數據庫有:MySQL、Oracle、PostgreSQL和 TiDB。
  6. 使用注解開啟分布式事務時,若默認服務 provider 端加入 consumer 端的事務,provider 可不標注注解。但是,provider 同樣需要相應的依賴和配置,僅可省略注解。
  7. 使用注解開啟分布式事務時,若要求事務回滾,必須將異常拋出到事務的發起方,被事務發起方的 @GlobalTransactional 注解感知到。provide 直接拋出異常 或 定義錯誤碼由 consumer 判斷再拋出異常。

 

 

2、AT 模式和 Spring @Transactional 注解連用時需要注意什么 ?

@Transactional 可與 DataSourceTransactionManager 和 JTATransactionManager 連用分別表示本地事務和XA分布式事務,大家常用的是與本地事務結合。當與本地事務結合時,@Transactional和@GlobalTransaction連用,@Transactional 只能位於標注在@GlobalTransaction的同一方法層次或者位於@GlobalTransaction 標注方法的內層。這里分布式事務的概念要大於本地事務,若將 @Transactional 標注在外層會導致分布式事務空提交,當@Transactional 對應的 connection 提交時會報全局事務正在提交或者全局事務的xid不存在。

 

3、怎么使用Seata框架,來保證事務的隔離性?

A: 因seata一階段本地事務已提交,為防止其他事務臟讀臟寫需要加強隔離。

    1. 臟讀 select語句加for update,代理方法增加@GlobalLock+@Transactional或@GlobalTransaction
    2. 臟寫 必須使用@GlobalTransaction
      注:如果你查詢的業務的接口沒有GlobalTransactional 包裹,也就是這個方法上壓根沒有分布式事務的需求,這時你可以在方法上標注@GlobalLock+@Transactional 注解,並且在查詢語句上加 for update。 如果你查詢的接口在事務鏈路上外層有GlobalTransactional注解,那么你查詢的語句只要加for update就行。設計這個注解的原因是在沒有這個注解之前,需要查詢分布式事務讀已提交的數據,但業務本身不需要分布式事務。 若使用GlobalTransactional注解就會增加一些沒用的額外的rpc開銷比如begin 返回xid,提交事務等。GlobalLock簡化了rpc過程,使其做到更高的性能。

 

 

4、臟數據回滾失敗如何處理?

A:

  1. 臟數據需手動處理,根據日志提示修正數據或者將對應undo刪除(可自定義實現FailureHandler做郵件通知或其他)
  2. 關閉回滾時undo鏡像校驗,不推薦該方案。

 

5、是否可以不使用conf類型配置文件,直接將配置寫入application.properties?

A: 目前seata-all是需要使用conf類型配置文件,后續會支持properties和yml類型文件。當前可以在項目中依賴seata-spring-boot-starter,然后將配置項寫入到application .properties 這樣可以不使用conf類型文件。

 

6、使用mybatis-plus 動態數據源組件后undolog無法刪除 ?

A:

dynamic-datasource-spring-boot-starter 組件內部開啟seata后會自動使用DataSourceProxy來包裝DataSource,所以需要以下方式來保持兼容

1.如果你引入的是seata-all,請不要使用@EnableAutoDataSourceProxy注解.

2.如果你引入的是seata-spring-boot-starter 請關閉自動代理

seata:
  enable-auto-data-source-proxy: false

 




 

(時間寶貴,分享不易,捐贈回饋,^_^)

 

================================

©Copyright 蕃薯耀 2021-05-10

https://www.cnblogs.com/fanshuyao/

 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM