Springboot单体版引入分布式事务Seata(不用nacos)

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值