一、Seata-Server

1.1 下载

官方下载地址https://seata.io/zh-cn/blog/download.html

1.2 启动

Seata-Server 是开箱即用的,启动 bin 目录下的 seata-server.bat 或者 seata-server.sh 即可

另外可以根据需求添加一些参数,输入 seata-server.sh --help 即可查看参数

1
2
3
4
5
6
7
8
9
10
11
12
13
Options:
--host, -h
注册到注册中心的ip
--port, -p
监听的端口,默认8091
--storeMode, -m
日志存储模式:文件、数据库
默认文件
--serverNode, -n
服务节点id,默认通过雪花算法生成
--seataEnv, -e
用于多配置隔离的环境名称
--help

seata-server 的默认启动端口是8091,如果我们要指定18091,只需 seata-server.sh -p 18091 即可

二、Spring Cloud 服务搭建

2.1 pom 依赖

1
2
3
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-seata</artifactId></dependency>

2.2 配置

2.2.1 registry.conf

resources 资源目录下配置 registry.conf 配置文件

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "file"

nacos {
application = "seata-server"
serverAddr = "localhost"
namespace = ""
username = ""
password = ""
}
eureka {
serviceUrl = "http://localhost:8761/eureka"
weight = "1"
}
redis {
serverAddr = "localhost:6379"
db = "0"
password = ""
timeout = "0"
}
zk {
serverAddr = "127.0.0.1:2181"
sessionTimeout = 6000
connectTimeout = 2000
username = ""
password = ""
}
consul {
serverAddr = "127.0.0.1:8500"
}
etcd3 {
serverAddr = "http://localhost:2379"
}
sofa {
serverAddr = "127.0.0.1:9603"
region = "DEFAULT_ZONE"
datacenter = "DefaultDataCenter"
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"
username = ""
password = ""
}
consul {
serverAddr = "127.0.0.1:8500"
}
apollo {
appId = "seata-server"
apolloMeta = "http://192.168.1.204:8801"
namespace = "application"
}
zk {
serverAddr = "127.0.0.1:2181"
sessionTimeout = 6000
connectTimeout = 2000
username = ""
password = ""
}
etcd3 {
serverAddr = "http://localhost:2379"
}
file {
name = "file.conf"
}
}

该文件指定了 seata 的注册中心和配置中心,如果没有注册中心和配置中心也可指定为文件的形式。最终指定的形式由 registry.typeconfig.type 来决定,如示例中就选择以 file 的形式来作为配置信息

2.2.2 file.conf

以 file 形式做注册和配置时,需在 resources 目录下创建 file.conf(registry.conf 中所配置的文件),如果采用其它的方式则不需要该文件。该文件主要配置 seata 的 ip、端口等信息,示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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
vgroup_mapping.my_test_tx_group = "default"
#only support when registry.type=file, please don't set multiple addresses
default.grouplist = "localhost: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
}
}

2.2.3 application.yml

除开数据源等其它配置,最主要的配置如下:

1
2
3
4
5
6
7
8
9
spring:
application:
# 应用名称
name: storage-gts-seata-example
cloud:
alibaba:
seata:
# 事务组名称
tx-service-group: my_test_tx_group

2.2.4 数据源配置

1
2
3
4
5
6
7
8
9
10
11
@Configuration
@ConditionalOnClass(DruidDataSource.class)
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
public class MyDataSourceConfig {

@Primary
@Bean("dataSource")
public DataSourceProxy dataSourceProxy(DataSource dataSource) {
return new DataSourceProxy(dataSource);
}
}

至此,Spring Cloud 整合 Seata 的基本完成,在事务发起方法上添加 @GlobalTransactional 注解即可

2.3 整合 Feign

分布式事务传播的过程中,需要通过判断事务id xid 来确认同一个事务。

Feign 默认使用线程池隔离,并且在发送请求时会丢弃父线程的请求头,导致 xid 无法正常传播。对此并没有必要改用信号量隔离,只需在过滤器中手动添加请求头即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class SeataXidFilter extends OncePerRequestFilter {
protected Logger logger = LoggerFactory.getLogger(SeataXidFilter.class);

public static final String XID_HEADER = "Xid_Header";

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String xid = RootContext.getXID();
String restXid = request.getHeader(XID_HEADER);
boolean bind = false;
if (StringUtils.isBlank(xid) && StringUtils.isNotBlank(restXid)) {
RootContext.bind(restXid);
bind = true;
if (logger.isDebugEnabled()) {
logger.debug("bind[" + restXid + "] to RootContext");
}
}
try {
filterChain.doFilter(request, response);
} finally {
if (bind) {
String unbindXid = RootContext.unbind();
if (logger.isDebugEnabled()) {
logger.debug("unbind[" + unbindXid + "] from RootContext");
}
if (!restXid.equalsIgnoreCase(unbindXid)) {
logger.warn("xid in change during http rest from " + restXid + " to " + unbindXid);
if (unbindXid != null) {
RootContext.bind(unbindXid);
logger.warn("bind [" + unbindXid + "] back to RootContext");
}
}
}
}
}
}

三、数据库脚本

在AT模式下需要创建 undo_log,在上面的 file.conf 中已经做了配置,对此我们需要在数据库中创建 undo_log 回滚日志表。不同数据库的脚本略有差异,以 MySQL 为例:

1
2
3
4
5
6
7
8
9
10
11
12
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,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

四、示例

官网中已经给了示例,自己做一个 AT 模式 SpringCloud版本的具体实现

仅供参考 无法作为实际业务逻辑