在搭建Seata分布式事务中遇到各种报错的解决方式

在成为项目负责人以后,前几个月一直忙于开发公司平台1.0,没有更新博客了。如今1.0的开发工作已经完成得七七八八,接下来需要搭建微服务的架构了。架构设计还是本人,接下来由这篇文章开始,记录我在搭建2.0平台微服务架构所踩的雷。

1. 服务端搭建

seata服务端的难点在于,需要配置的东西比较多,改各类的配置文件,接下来我给大家说说是哪些地方,关于seata服务端的搭建,由于使用了微服务,我们可以把Seata Server注册到nacos做配置统一管理,具体步骤如下:
首先我们要修改一下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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
registry {
# seata Server端启动时会先加载type,根据type 去找对应的配置
#file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "nacos"

nacos {
application = "seata-server"
serverAddr = "注册中心ip:8848"
group = "SEATA_GROUP"
namespace = ""
cluster = "default"
username = ""
password = ""
}
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"
sessionTimeout = 6000
connectTimeout = 2000
username = ""
password = ""
}
consul {
cluster = "default"
serverAddr = "127.0.0.1:8500"
aclToken = ""
}
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"
}
# 会根据 type = file 找到这里,然后再去读取"file.conf"
file {
name = "file.conf"
}
}

config {
# file、nacos 、apollo、zk、consul、etcd3
type = "nacos"

nacos {
serverAddr = "注册中心ip:8848"
namespace = ""
group = "SEATA_GROUP"
username = ""
password = ""
}
consul {
serverAddr = "127.0.0.1:8500"
aclToken = ""
}
apollo {
appId = "seata-server"
## apolloConfigService will cover apolloMeta
apolloMeta = "http://192.168.1.204:8801"
apolloConfigService = "http://192.168.1.204:8080"
namespace = "application"
apolloAccesskeySecret = ""
cluster = "seata"
}
zk {
serverAddr = "127.0.0.1:2181"
sessionTimeout = 6000
connectTimeout = 2000
username = ""
password = ""
nodePath = "/seata/seata.properties"
}
etcd3 {
serverAddr = "http://localhost:2379"
}
file {
name = "file.conf"
}
}

其实没用的配置可以把它删掉,这里不多演示。
接下来是最关键的,获取/seata源码/script/config-center/config.txt,修改config.txt配置信息:

1
2
3
4
5
6
7
8
store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.cj.jdbc.Driver
store.db.url=jdbc:mysql://xxx:3306/seata?useUnicode=true&rewriteBatchedStatements=true
store.db.user=nacos
store.db.password=Springbootv2
service.vgroupMapping.guangzhou=default

⭐⭐⭐上面给出的都是我们需要改的,这里注意的是,配置事务分组, 要与客户端配置的事务分组一致,这个非常重要,如果不对应的话,客户端会找不到服务端服务。
服务端最后一步就是,在cmd窗口中执行shell脚本,会自动把config.txt文件的配置信息推送到nacos对应的namespace下,这边给出一个例子:

1
sh ${SEATAPATH}/script/config-center/nacos/nacos-config.sh -h localhost -p 8848 -g SEATA_GROUP -t 5a3c7d6c-f497-4d68-a71a-2e5e3340b3ca

⭐参数说明:
-h: IP地址:默认值 localhost
-p: 端口号:默认值 8848
-g: 配置分组:默认值为 ‘SEATA_GROUP’
-t: 租户信息:对应 Nacos 的命名空间ID字段, 默认值为空
大功告成!可以烧个小烟花,因为脏活搞完了。

2. 客户端搭建

相比于服务端搭建,客户端就显得so easy了,只需要在原有的依赖上加入seata依赖就好了,由于我们父maven定义了版本控制,所以也不需要搞版本了。

1
2
3
4
5
<!-- seata-->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
</dependency>

接下来,要为每一个服务的数据库创建undo_log表,这个表示用来记录回滚的,这里的sql文件在seata的源代码文件里面有,这里就不唠叨了。
最后配置一下yml,启动类上需要排除DataSourceAutoConfiguration,否则会出现循环依赖的问题,业务代码上面加个 @GlobalTransactional的注解就能用分布式事务了,这边比较简单不再细讲,然后贴一下yml里面改什么:

1
2
3
4
5
6
7
8
9
10
11
spring:
application:
name: order-service
cloud:
nacos:
discovery:
server-addr: 注册中心ip:8848
alibaba:
seata:
tx-service-group:
my_test_tx_group # seata 服务事务分组

3. 序列化问题

这个问题出现的也是比较常见,但是我看网上很少解决方式,因此我在这边说一下我的解决方式。

seata1
当报这个错误的时候,如报错信息所示,是因为我们没有引入一个叫kryo的依赖,那么为什么会这样呢?上网一查,原因是这样的:
在Spring Boot 1.5x 版本中原始引入的Jackson版本过低,会导致Seata依赖Jackson的新特性找不到,Seata要求Jackson版本2.9.9+,但是使用Jackson 2.9.9+版本会导致Spring Boot中使用的Jackson API找不到,也就是Jackson本身的向钱兼容性存在问题。因此,建议大家将Seata的序列化方式切换到非Jackson序列化方式,比如kryo。
好了,说了那么多,其实也很好理解,其实就加个依赖嘛,那就直接干。

1
2
3
4
5
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
<version>4.0.2</version>
</dependency>

引入完以后,项目重启,依然报错:

seata2

由报错信息很容易看出(NoClassDefFoundErrer),还缺了依赖,那么我们继续引入依赖

1
2
3
4
5
<dependency>
<groupId>de.javakaffee</groupId>
<artifactId>kryo-serializers</artifactId>
<version>0.44</version>
</dependency>

重启后验证功能,功能正常,成功解决!

4. AT模式回滚问题

关于这个回滚的问题,很多新手会说:“咦,seata数据库里面的global_table、branch_table 、lock_table、undo_log表里面怎么没有数据?”
别着急,因为回滚得太快了,你要打个断点debug运行一下就可以看到数据库表里面有插入和删除操作的了。