分布式事务Seata1.3搭建集成(nacos、mybatis plus)


微服务环境下,分布式事务不得不考虑,因为整体框架本身偏向cloud alibaba,所以就用了seata来做分布式管理,这里使用的是AT模式,记录一下集成步骤和遇到的问题。

1 项目环境

这里版本参考了cloud alibaba版本说明

组件版本
spring-boot2.1.13.RELEASE
spring-cloudGreenwich.SR6
spring-cloud-alibaba2.1.4.RELEASE
nacos1.4.1
seata1.3.0
MySQL5.7
mybatis plus2.1.0

2 seata-server搭建

2.1 下载

这里源码和打包好的server包都下载下来,官网和github都可以
seata官网下载页
seata github下载页

2.2 建库建表

新建一个数据库给seata服务端用,数据库名自己起

-- 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;

附上sql来源

2.3 安装配置

这里偷个懒就用window环境演示了,用linux还是docker安装,应该都差不多,都是改改配置

2.3.1 将配置上传到nacos

把下载下来的seata1.3.0源码用idea打开,找到config.txt和nacos-config.sh,这两个是要用到的。
或者在github也可以获取到:config.txt nacos-config.sh
在这里插入图片描述
首先打开config.txt,修改如下配置:
在这里插入图片描述
上面的是事务组的名字,后面要用,下边的就是把之前建的数据库信息配置进去,修改好后把这两个文件复制出来。
解压之前下载好的seata-server,把config.txt复制到根路径下,nacos-config.sh复制到bin或者conf目录下
在这里插入图片描述
在nacos-config.sh所在目录下打开git bash窗口
在这里插入图片描述
输入命令

sh nacos-config.sh -h 你的nacosIP -p 你的nacos端口 -t 推送目标namespace -g 推送目标group

成功后提示 init nacos config finished, please start seata-server,这个时候登录nacos 就可以看到推送上去的配置了
在这里插入图片描述

2.3.2 server配置修改

打开conf/registry.conf文件
修改信息,保留nacos配置

registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "nacos"

  nacos {
    application = "seata-server"
    serverAddr = "192.168.3.88:10210"
    group = "ES_GROUP"
    namespace = "96839b27-3eec-4729-8723-84fe07a71a4a"
    cluster = "default"
    username = "nacos"
    password = "123456"
  }
  
}

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

  nacos {
    serverAddr = "192.168.3.88:10210"
    namespace = "96839b27-3eec-4729-8723-84fe07a71a4a"
    group = "ES_GROUP"
    username = "nacos"
    password = "123456"
  }
  
}

修改完成后,到bin目录下运行脚本启动seata,提示:Server started, listen port: 8091
到nacos里可以看到,名为seata-server的服务已经注册进来了,至此seata-server搭建完成
在这里插入图片描述

3 服务集成seata

3.1 添加undo_log表

参加事务的业务服务数据库需要添加数据库表,github获取地址

CREATE TABLE IF NOT EXISTS `undo_log`
(
    `branch_id`     BIGINT(20)   NOT NULL COMMENT 'branch transaction id',
    `xid`           VARCHAR(100) NOT NULL COMMENT 'global transaction id',
    `context`       VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
    `rollback_info` LONGBLOB     NOT NULL COMMENT 'rollback info',
    `log_status`    INT(11)      NOT NULL COMMENT '0:normal status,1:defense status',
    `log_created`   DATETIME(6)  NOT NULL COMMENT 'create datetime',
    `log_modified`  DATETIME(6)  NOT NULL COMMENT 'modify datetime',
    UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB
  AUTO_INCREMENT = 1
  DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';

3.2 引入依赖

 <!-- Seata -->
 <dependency>
     <groupId>com.alibaba.cloud</groupId>
     <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
 </dependency>

3.3 配置文件

seata:
  enabled: true
  application-id: ${spring.application.name}
  tx-service-group: es_tx_group # 这里事务组的名称是config.txt里配置的,已推送到nacos
  enable-auto-data-source-proxy: true
  config:
    type: nacos
    nacos:
      serverAddr: 192.168.3.88:10210
      namespace: 96839b27-3eec-4729-8723-84fe07a71a4a
      group: ES_GROUP
      userName: "nacos"
      password: "123456"
  registry:
    type: nacos
    nacos:
      application: seata-server
      server-addr: 192.168.3.88:10210
      namespace: 96839b27-3eec-4729-8723-84fe07a71a4a
      group: ES_GROUP
      userName: "nacos"
      password: "123456"

3.4 加入事务控制

在代码方法前加上@GlobalTransactional注解即可,然后启动项目即可,这里就不演示了(因为会报错哈哈哈,问题在第四章描述)
在这里插入图片描述

3.4 启动项目

可见事务注册信息
在这里插入图片描述

4 问题及处理

4.1 zipkin+seata导致openfeign调用出错

4.1.1 描述

调用接口时,会发现服务在nacos上正常,但feign调用崩了

com.netflix.client.ClientException: Load balancer does not have available server for client

4.1.2 原因

项目 pom 引入的 Zipkin 包含 Sleuth,而 Sleuth 的配置类TraceFeignClientAutoConfiguration 和 Seata 的配置类 SeataFeignClientAutoConfiguration 都创建了 Bean:feignHystrixBuilder,冲突导致上面的错误

4.1.3 解决办法

启动类上排除SeataFeignClientAutoConfiguration

@SpringBootApplication(exclude = {SeataFeignClientAutoConfiguration.class})

使用分布式事务的业务服务,加上拦截器传递XID

import feign.RequestInterceptor;
import feign.RequestTemplate;
import io.seata.core.context.RootContext;
import io.seata.spring.annotation.GlobalTransactional;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.stereotype.Component;

/**
 * @Description 传递事务XID
 * @Author LX
 * @Date 2021-07-21 15:42
 */
@Component
@ConditionalOnClass({RequestInterceptor.class, GlobalTransactional.class})
public class SetSeataInterceptor implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate template) {
        String currentXid = RootContext.getXID();
        if (!StringUtils.isEmpty(currentXid)) {
            template.header(RootContext.KEY_XID, currentXid);
        }
    }
}

这样就可以正常进行feign调用了

4.2 mybatis plus组件失效

4.2.1 描述

参考了seata提供的例子,用到mybatis plus的小伙伴可能会为了适配,配置过一个类似于这样的类,但是配置过后发现seata之后,mybatis的分页插件好像失效了。

@Configuration
public class DataSourceProxyConfig {

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource() {
        return new DruidDataSource();
    }

    @Bean
    @ConfigurationProperties(prefix = "mybatis-plus")
    public MybatisSqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) throws IOException {
        // 这里用 MybatisSqlSessionFactoryBean 代替了 SqlSessionFactoryBean,否则 MyBatisPlus 不会生效
        MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
        mybatisSqlSessionFactoryBean.setDataSource(dataSource);
        mybatisSqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath*:/mapper/mapper/*.xml"));
        return mybatisSqlSessionFactoryBean;
    }
}

4.2.2 原因

虽然这里为了适配mybatis plus,将返回值改为了MybatisSqlSessionFactoryBean,但是同样需要在这个方法里加入分页插件再返回(但实际上是多此一举)
可以参考seata官网FAQ中的描述
在这里插入图片描述
可以看到我们依赖的是seata-spring-boot-starter,且在前面的配置文件中也写入了enable-auto-data-source-proxy: true,所以我们没必要再做数据源代理的配置了。
在这里插入图片描述

4.2.3 解决办法

把上面的配置文件干掉!

5 结语

由于本人技术水平有限或对组件理解不到位,上述内容可能有纰漏或者错误,欢迎各位大佬指正!
因为自己一直不善于去整理记录东西,工作中也会遇到很多问题,总是做完就结束。后面遇到同样的问题的时候,还是会忘记或者再次踩坑,所以想慢慢的让自己慢慢的养成记录的习惯,好记性不如烂笔头!

6 参考

[1] seata 官网:http://seata.io/zh-cn/index.html
[2] seata github:https://github.com/seata/seata
[3] 博客:https://blog.csdn.net/ZaiZuoYuZuo/article/details/107636180
[4] 博客:https://blog.csdn.net/qq_35721287/article/details/103282589

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Seata的高可用模式是通过TC使用db模式共享全局事务会话信息,使用非file的seata支持的第三方注册中心和配置中心来共享全局配置的方式来实现的。 Seata支持的第三方注册中心有nacos 、eureka、redis、zk、consul、etcd3、sofa、custom,支持的第三方配置中心有nacos 、apollo、zk、consul、etcd3、custom。seata官方主推的应该是nacos(毕竟是一家的),但是由于本人平常使用的注册中心一直是zk,因此首先考虑通过zk作为注册中心来实现高可用模式。 环境准备 zk环境准备 本地已安装zk的可以忽略,如果本地未安装,先在本地安装zk,具体安装自行百度。 PS: 此处如果使用的是远程zk服务器,则本地可以只下载,不安装。 数据库环境准备 1、创建数据库seata 2、执行源码(version1.2.0)script -> server -> db 下面的mysql.sql文件,建立global_table,branch_table,lock_table表。 配置信息导入zk 1、打开本地seata源码(版本1.2.0) 2、编辑script -> config-center -> config.txt文件,修改store.mode=db,修改store.db相关的数据库连接信息,其它默认即可 3、进入script -> config-center ->zk,执行 sh zk-config.sh -h 127.0.0.1 -p 2181 -z /usr/local/zookeeper-3.4.14(-z 后面的参数为你本地zk目录) 上面命令会将config.txt中的配置信息写入zk的/seata节点下。 启动tc-server 1、编辑conf下面的registry.conf文件,修改registry.type=zk,修改config.type=zk,修改registry.zk及config.zk信息,如下: 注意:config的zk配置没有cluster属性。 2、启动server,在本地seata安装目录bin目录下执行./seata-server.sh -m db (此处也可以直接编译本地源码然后启动Server模块下的Server类)。 不出意外的话,启动会报错,错误信息是从zk读取配置的时候反序列化出问题。 错误原因:序列化问题,由于使用seata自带的zk-config.sh脚本向zk写入配置信息的时候,采用的序列化方式相当于String.getBytes(),而框架读取配置的时候使用的是org.101tec包中的Zkclient客户端,反序列化使用的是该包下面的SerializableSerializer序列化类,使用的ObjectOutputStream进行反序列化,和序列化方式不一致。 该问题在1.3.0版本中解决,解决方式是seata支持序列化方式配置,支持自定义序列化方式,同时提供默认序列化实现类DefaultZkSerializer,反序列化实现为new String()。 到此处,1.2.0版本无法进行下去,由于目前1.3.0正式版本还未出,只能拉取最新的开发分支源码,本地编译打包1.3.0-SNAPSHOT版本。 后续版本切为1.3.0-SNAPSHOT(20200701),删除原zk配置信息重新导入1.3版本的config.txt文件信息。 本地源码编译后直接Idea启动Server类。启动成功。 PS:启动日志里面会有一些getConfig失败的报错,这些不用管,这些新的配置参数是1.3版本新增的,由于当前是SNAPSHOT版本,还不完善。 PS: 如果遇到getServerCharset 空指针异常,这个主要是MySQL和MySQL驱动版本不一致引起的,看https://blog.csdn.net/zcs20082015/article/details/107087589 服务启动 配置修改 简单处理,这里不再建新的模块,直接使用zhengcs-seata-storage模块作为演示。 1、修改POM,引入zkclient,修改seata版本 2、修改application.yml,将注册和配置类型改为zk 另外需要注意的是seata.tx-service-group配置参数要和zk导入的配置信息相关参数保持一致,否则会找不到server集群 启动服务 1、引入全局事务 2、启动 测试 基本功能测试 单元测试用例: 手动插入异常 执行用例: 基本功能是没问题的,更详细全面的测试这里就不介绍了,大家自行尝试。 高可用测试 上面的单机版肯定无法满足高可用,tc-server一旦宕掉,整个全局事务会无法进行回滚,同时会在seata库下面的事务表里面留下事务记录(正常处理成功后会被删除)。 seata的高可用是通过多个tc-server实例组成的集群来实现的。 启动多个tc-server实例: 通过-p参数修改启动接口,同时勾选Allow parallel run,开启多个实例。 然后启动客服端服务: 从启动日志可以看出,客户端会同时向所有几点注册TM和RM。 执行测试用例: 那,如果在数据已提交,异常退出之前把对应的tc-server节点停掉,会怎么样呢?答案是:全局事务回滚。大家自行尝试一下。 还有一种情况,如果客户端在执行过程中中断了,会怎么样? 如果客户端是单节点部署,那么: 首先,seata库下面的事务处理表里面有遗留事务处理记录,然后你会发现tc-server端日志里面会持续刷上述日志,tc-server会持续的尝试回滚该事务。最遗憾的是:哪怕客户端服务重启,也不会回滚该事务!!! 不过还好的是,这种情况毕竟是特例,如果客户端服务是集群部署,那么seata是可以正常完成事务回滚的。 结语 从上面的情况来看,起码seata对于简单的分布式事务场景的高可用支撑是没问题的,但是seata毕竟还是一个新框架,在实际的复杂的业务场景下会否出现什么问题,其实应该说出现什么问题都是可能的,这个需要实践和时间才能出真知了。 另外,seata目前没有提供控制台,没有服务集群配套的HA机制,这个不知道什么时候会出,拭目以待,seata应该会是一个很不错的分布式事务解决方案。   参考资料 https://seata.io/zh-cn/docs/ https://github.com/seata/seata ———————————————— 版权声明:本文为CSDN博主「zhengcs已被占用」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/zcs20082015/article/details/107092936

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值