目錄
說明
謹以此記錄學習 Seata 時踩過的坑
項目是一個微服務的架構,需要使用到“分布式事務”,在折騰了tx-lcn、tcc-transaction等幾個玩意兒后,最終把目光定格在seata上,決定用seata給基於Eureka+Feign的項目添加分布式事務管理。
資料如下:
步驟
-
下載、配置、運行 Seata Server
Seata 需要使用 Server 端的配合來實現分布式事務,因此需要先部署 Seata 的 Server 環境。 -
配置共同環境
服務的調用方以及服務提供方,都需要進行一些共同的、一致的配置。 -
配置各自的環境
其實就是給服務調用方加上全局事務(分布式事務)的注解"@GlobalTransactional",服務提供方不需要額外配置。
實戰
下載、配置並運行SeataServer
下載
下載地址:
http://seata.io/zh-cn/blog/download.html
Seata 1.2.0 (2020-04-20) 目錄結構如下:
bin用於運行Seata Server; lib是server用到的資源庫,無需理會; conf是配置文件目錄。
bat為win下腳本文件; sh為unix-like系統下的腳本。
關注file.conf與register.conf
配置
根據自己需求修改配置文件—— "file.conf" 和 "registry.conf"。文章開頭已貼出服務端以及應用端(客戶端)配置項的說明的地址。
file.conf
此次需要用到數據庫來保存 "undo_log" 等等的數據,需要在"file.conf"修改保存模式"mode"為"db",修改"db"中數據庫相關的配置:
store {
## store mode: file、db
mode = "db"
## database store property
db {
## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
datasource = "druid"
## mysql/oracle/postgresql/h2/oceanbase etc.
dbType = "mysql"
driverClassName = "com.mysql.jdbc.Driver"
url = "jdbc:mysql://localhost:3306/seata_server"
user = "root"
password = "root"
minConn = 5
maxConn = 30
globalTable = "global_table"
branchTable = "branch_table"
lockTable = "lock_table"
queryLimit = 100
maxWait = 5000
}
}
mode = "db" 表明事務信息用db存儲,當mode="db"時,只有db節點下的配置生效,不用管file節點
registry.conf
同樣根據自己需求修改。
seata server會把自己注冊到注冊中心,像其它的微服務模塊一樣。registry.conf就是配置seata server使用何種注冊中心以及如何注冊。
registry節點為服務注冊中心的類型及配置,此處選擇type=eureka並修改對應的eureka的配置。
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "eureka"
eureka {
serviceUrl = "http://localhost:8761/eureka"
application = "default"
weight = "1"
}
}
#配置中心的配置,根據自己需求修改,eureka不支持充當配置中心,保留默認值
config {
# file、nacos 、apollo、zk、consul、etcd3
...
...
}
運行
根據操作系統類型選擇腳本運行。
基本環境的配置
由於此處使用數據庫存儲事務相關的數據,所以應先建庫、建表。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(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 = InnoDB
DEFAULT CHARSET = utf8;
調用方和服務方的共同配置
數據庫
以上是基本的數據表。另外,每個被調用的服務以及服務的調用方都需要用到 "undo_log"表用於事務的回滾,sql如下:
-- 注意此處0.3.0+ 增加唯一索引 ux_undo_log
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;
maven依賴:
<!--seata-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-seata</artifactId>
<version>2.1.0.RELEASE</version>
<exclusions>
<exclusion>
<artifactId>seata-all</artifactId>
<groupId>io.seata</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>1.2.0</version>
</dependency>
其它的配置文件
需要在resource目錄下創建 "file.conf" 和 "registry.conf" 文件。這兩個文件與之前下載的seata目錄里面的配置文件不是一樣的,兩者內容如下:
file.conf
transport {
# tcp udt unix-domain-socket
type = "TCP"
#NIO NATIVE
server = "NIO"
#enable heartbeat
heartbeat = true
# the client batch send request enable
enableClientBatchSendRequest = true
#thread factory for netty
threadFactory {
bossThreadPrefix = "NettyBoss"
workerThreadPrefix = "NettyServerNIOWorker"
serverExecutorThread-prefix = "NettyServerBizHandler"
shareBossWorker = false
clientSelectorThreadPrefix = "NettyClientSelector"
clientSelectorThreadSize = 1
clientWorkerThreadPrefix = "NettyClientWorkerThread"
# netty boss thread size,will not be used for UDT
bossThreadSize = 1
#auto default pin or 8
workerThreadSize = "default"
}
shutdown {
# when destroy server, wait seconds
wait = 3
}
serialization = "seata"
compressor = "none"
}
service {
#transaction service group mapping
vgroupMapping.my_test_tx_group = "default"
#only support when registry.type=file, please don't set multiple addresses
#關於grouplist,只有當registry.type=file,注冊中心是file方式時,才會起作用。 https://blog.csdn.net/weixin_39800144/article/details/100726116
default.grouplist = "127.0.0.1:8091"
#degrade, current not support
enableDegrade = false
#disable seata
disableGlobalTransaction = false
}
client {
rm {
asyncCommitBufferLimit = 10000
lock {
retryInterval = 10
retryTimes = 30
retryPolicyBranchRollbackOnConflict = true
}
reportRetryCount = 5
tableMetaCheckEnable = false
reportSuccessEnable = false
}
tm {
commitRetryCount = 5
rollbackRetryCount = 5
}
undo {
dataValidation = true
logSerialization = "jackson"
logTable = "undo_log"
}
log {
exceptionRate = 100
}
}
registry.conf:
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "eureka"
nacos {
serverAddr = "localhost"
namespace = ""
cluster = "default"
}
eureka {
serviceUrl = "http://localhost:8761/eureka"
application = "default"
weight = "1"
}
redis {
serverAddr = "localhost:6379"
db = "0"
password = ""
cluster = "default"
timeout = "0"
}
zk {
cluster = "default"
serverAddr = "127.0.0.1:2181"
session.timeout = 6000
connect.timeout = 2000
username = ""
password = ""
}
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 {
# file、nacos 、apollo、zk、consul、etcd3、springCloudConfig
type = "file"
nacos {
serverAddr = "localhost"
namespace = ""
group = "SEATA_GROUP"
}
consul {
serverAddr = "127.0.0.1:8500"
}
apollo {
app.id = "seata-server"
apollo.meta = "http://192.168.1.204:8801"
namespace = "application"
}
zk {
serverAddr = "127.0.0.1:2181"
session.timeout = 6000
connect.timeout = 2000
username = ""
password = ""
}
etcd3 {
serverAddr = "http://localhost:2379"
}
file {
name = "file.conf"
}
}
可根據自己需求修改,一般情況下默認值即可。
參考:
application.yml的配置
application.yml:
spring:
cloud:
alibaba:
seata:
tx-service-group: my_test_tx_group
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/xx
username: root
password: root
啟動類的配置
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
seata 需要使用代理的 DataSource, 因此不使用 DataSourceAutoConfiguration
事務的實現
基本的配置已經實現,被調用的服務的方法(即服務提供方的方法)不需要做額外的配置,只需要在調用方(即需要通過feign調用其它服務)的方法上添加 "@GlobalTransactional" 注解,如:
@GlobalTransactional(name = "fsp-create-order", rollbackFor = Exception.class)
public void createOrder(Param param){
//通過feign調用其它服務
accountFeign.checkAccount();
productFeign.checkNum();
}
拓展
在client端的配置文件"file.conf"中,可以看到如上的service節點,紅框部分是事務組的名稱,官方說該名稱應該和Server中的配置保持一致,然而在1.2.0版本的seata server端的配置文件中找不到該配置項...所以此處保留了sample的名稱,此時可以運行成功。seata的文檔真是一言難盡...
項目是使用eureka作為注冊中心,而在文章開頭也提到對seata配置文件registry.conf的修改,把type改為"eureka",即把seata server注冊到eureka中,此時,"vgroupMapping.my_test_tx_group"的值"default",就是seata server在eureka中的Application name:
參考: