Springboot+Seata(不用nacos)
单体spring项目使用seata原因:
工作中有个springboot单体项目由于涉及多数据源,多数据源切换会导致普通的Transactional事务失效(数据库链接复用的原因,有兴趣自查),之前一直通过逻辑设计,规避多数据源切换导致的事务失败问题,最近看不下去为了规避这个问题带来的代码结构(毕竟不是什么重要项目模块),还是决定引入seata,但是单体项目又没必要用nacos或者zookeeper,所以采用springboot+seata+本地file的方式实现分布式事务。期间发现了一些报错,下面正文开始。
先说常见问题
使用Oracle时间类数据类型提示序列化异常
Type id handling not implemented for type java.lang.Object (by serializer of type com.fasterxml.jackson.databind.ser.impl.UnsupportedTypeSerializer) (through reference chain: io.seata.rm.datasource.undo.BranchUndoLog["sqlUndoLogs"]->java.util.ArrayList[0]->io.seata.rm.datasource.undo.SQLUndoLog["afterImage"]->io.seata.rm.datasource.sql.struct.TableRecords["rows"]->java.util.ArrayList[0]->io.seata.rm.datasource.sql.struct.Row["fields"]->java.util.ArrayList[16]->io.seata.rm.datasource.sql.struct.Field["value"])
或者报错
JacksonUndoLogParser supports to parsing LocalDateTime
或者
Class cannot be created (missing no-arg constructor): java.time.LocalDateTime
看过几个解决方案,比如引入kryo(只能解决seata启动报错的问题,无法解决时间序列化问题)、什么将undo_log表中blob类型改成string类型都无法解决实际问题。
原因分析:
在undoLog序列化为JSON的时候,LocalDateTime无法序列化,源码在JacksonUndoLogParser,查看JacksonUndoLogParser源码后发现仅增加Timestamp类的序列化和反序列化处理,对LocalDateTime的序列化和反序列化未做处理
解决方案:
一.自己实现LocalDateTime序列化和反序列化spi
也就是实现JacksonSerializer接口,即可实现序列化和反序列化。
新建LocalDateTimeJacksonSerializer(忽略我这里放的位置不太好,是这么个意思就行)
package io.seata.rm.datasource.undo.parser.spi;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class LocalDateTimeJacksonSerializer implements JacksonSerializer<LocalDateTime> {
public static final String NORM_DATETIME_MS_PATTERN = "yyyy-MM-dd HH:mm:ss.SSS";
public LocalDateTimeJacksonSerializer() {
}
public Class<LocalDateTime> type() {
return LocalDateTime.class;
}
public JsonSerializer<LocalDateTime> ser() {
return new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));
}
public JsonDeserializer<? extends LocalDateTime> deser() {
return new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));
}
}
涉及修改jar包,提供一个好用IDEA插件工具:JarEditor(自查使用)
二.在resources META-INF目录下创建spi对应关系
新建seata/io.seata.rm.datasource.undo.parser.spi.JacksonSerializer文件
,加入我们刚刚实现的LocalDateTimeJacksonSerializer
内容如下,填入LocalDateTimeJacksonSerializer类的完整路径
io.seata.rm.datasource.undo.parser.spi.LocalDateTimeJacksonSerializer
以上就能解决对应的序列化问题了
.
下面给出我seata的配置
一.pom引入
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.4.2</version>
</dependency>
二.springboot中yaml
seata:
enabled: true
application-id: default
tx-service-group: default
enable-auto-data-source-proxy: true
config:
type: file
file:
data-file: classpath:file.conf
registry:
type: file
file:
data-file: classpath:registry.conf
三.resources下file.conf和registry.conf
file.conf如下
service {
# tx-service-group映射
vgroupMapping.my_test_tx_group = "default"
vgroupMapping.default = "default"
# Seata Server的地址,是你的Seata Server所在的IP地址和端口
default.grouplist = "127.0.0.1:8091"
# 其他相关配置
enableDegrade = false
disableGlobalTransaction = false
}
store {
mode = "file"
file {
# 本地事务日志存储的目录
dir = "sessionStore"
}
}
registry.conf如下:
registry {
type = "file"
file {
name = "file.conf"
}
}
config {
type = "file"
file {
name = "file.conf"
}
}
四.seata 服务端配置如下
修改registry.conf文件内容
## transaction log store, only used in seata-server
service {
vgroupMapping.my_test_tx_group = "default"
default.grouplist = "127.0.0.1:8091"
enableDegrade = false
disable = false
}
store {
## store mode: file、db、redis
mode = "file"
## rsa decryption public key
publicKey = ""
## file store property
file {
## store location dir
dir = "sessionStore"
# branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
maxBranchSessionSize = 16384
# globe session size , if exceeded throws exceptions
maxGlobalSessionSize = 512
# file buffer size , if exceeded allocate new buffer
fileWriteBufferCacheSize = 16384
# when recover batch read size
sessionReloadReadSize = 100
# async, sync
flushDiskMode = async
}
## database store property
db {
## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
datasource = "druid"
## mysql/oracle/postgresql/h2/oceanbase etc.
dbType = "mysql"
driverClassName = "com.mysql.jdbc.Driver"
## if using mysql to store the data, recommend add rewriteBatchedStatements=true in jdbc connection param
url = "jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true"
user = "mysql"
password = "mysql"
minConn = 5
maxConn = 100
globalTable = "global_table"
branchTable = "branch_table"
lockTable = "lock_table"
queryLimit = 100
maxWait = 5000
}
## redis store property
redis {
## redis mode: single、sentinel
mode = "single"
## single mode property
single {
host = "127.0.0.1"
port = "6379"
}
## sentinel mode property
sentinel {
masterName = ""
## such as "10.28.235.65:26379,10.28.235.65:26380,10.28.235.65:26381"
sentinelHosts = ""
}
password = ""
database = "0"
minConn = 1
maxConn = 10
maxTotal = 100
queryLimit = 100
}
}
file.conf
## transaction log store, only used in seata-server
service {
vgroupMapping.my_test_tx_group = "default"
default.grouplist = "127.0.0.1:8091"
enableDegrade = false
disable = false
}
store {
## store mode: file、db、redis
mode = "file"
## rsa decryption public key
publicKey = ""
## file store property
file {
## store location dir
dir = "sessionStore"
# branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
maxBranchSessionSize = 16384
# globe session size , if exceeded throws exceptions
maxGlobalSessionSize = 512
# file buffer size , if exceeded allocate new buffer
fileWriteBufferCacheSize = 16384
# when recover batch read size
sessionReloadReadSize = 100
# async, sync
flushDiskMode = async
}
}
后续:查阅seata项目pr确实1.5.1版本后就已经优化该问题了,大家可以试试直接试试seata1.5以上版本
参考:
https://juejin.cn/post/7129388116908965919
https://www.cnblogs.com/better-farther-world2099/articles/16153045.html
https://blog.csdn.net/kaige8312/article/details/117411341
https://blog.csdn.net/qq_43437874/article/details/136739085
https://github.com/apache/incubator-seata/issues/4350