Spring Cloud与Seata 2.0集成:零基础安装到AT模式实战的完整教程

文章首发微信公众号:【Coder-文小白】,欢迎大家进行技术交流。

一、seata框架简介

SeataSimple Extensible Autonomous Transaction Architecture)是一个开源的分布式事务解决方案,旨在解决微服务架构下的事务一致性问题。它提供了高性能、易扩展的分布式事务服务,帮助开发者在分布式系统中实现事务管理。Seata 2.0版本是这一框架的一个重要更新,带来了更多的功能和优化。例如,Seata 2.0版本继续优化了事务处理的性能,通过更高效的事务日志存储方式和更精细的并发控制策略,提高了事务处理的吞吐量和响应速度。

更多关于seata2.0版本新特性,可以通过官网(https://seata.apache.org/zh-cn/)进行了解。

二、安装准备

本次我们安装过程中使用nacos作为注册中心和配置中心,所以需要部署好nacos环境;同时seata框架需要依赖于数据库,需要准备好数据库环境,数据库环境的搭建比较简单,只需要运行seata的初始化SQL脚本,进行数据库连接即可,数据库的安装不再赘述,可以自行安装。

1. nacos部署

介质下载地址:https://download.nacos.io/nacos-server/nacos-server-2.4.0.1.zip

下载完成以后,进行安装包解压,解压完成以后,目录参考如下:

在这里插入图片描述

进入bin目录,通过单机方式启动nacos

startup.cmd -m standalone

启动完成以后,通过浏览器访问 http://localhost:8848/nacos 地址,能进入nacos管控台则说明启动成功。

2. seata部署

介质下载地址:https://github.com/apache/incubator-seata/releases/download/v2.0.0/seata-server-2.0.0.zip

下载完成以后,进行安装包解压,解压完成以后,目录参考如下:
在这里插入图片描述

进入到script\server\db目录下,根据自身的数据库环境选择不同的SQL初始化脚本,进行数据库初始化:

在这里插入图片描述

我本地环境是mysql数据库,首先创建一个数据库:

CREATE DATABASE `seata` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */;

然后执行mysql.sql里面的SQL脚本:

-- -------------------------------- The script used when storeMode is 'db' --------------------------------
-- 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_status_gmt_modified` (`status` , `gmt_modified`),
    KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4;

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

-- the table to store lock data
CREATE TABLE IF NOT EXISTS `lock_table`
(
    `row_key`        VARCHAR(128) NOT NULL,
    `xid`            VARCHAR(128),
    `transaction_id` BIGINT,
    `branch_id`      BIGINT       NOT NULL,
    `resource_id`    VARCHAR(256),
    `table_name`     VARCHAR(32),
    `pk`             VARCHAR(36),
    `status`         TINYINT      NOT NULL DEFAULT '0' COMMENT '0:locked ,1:rollbacking',
    `gmt_create`     DATETIME,
    `gmt_modified`   DATETIME,
    PRIMARY KEY (`row_key`),
    KEY `idx_status` (`status`),
    KEY `idx_branch_id` (`branch_id`),
    KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4;

CREATE TABLE IF NOT EXISTS `distributed_lock`
(
    `lock_key`       CHAR(20) NOT NULL,
    `lock_value`     VARCHAR(20) NOT NULL,
    `expire`         BIGINT,
    primary key (`lock_key`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4;

INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('AsyncCommitting', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryCommitting', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryRollbacking', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('TxTimeoutCheck', ' ', 0);

nacso和数据库环境准备好以后,接下来就是修改seata相关配置了,重新进入conf目录,找到application.yml文件,可以参考目录下application.example.yml文件进行配置修改,我本地环境是使用nacos作为注册中心和配置中心,数据库环境是mysql 8.0,修改以后参考配置如下:

#  Copyright 1999-2019 Seata.io Group.
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#  http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

server:
  port: 7091

spring:
  application:
    name: seata-server

logging:
  config: classpath:logback-spring.xml
  file:
    path: E:\\00-distribute-tx\\seata\\logs # 修改日志存储路径
  extend:
    logstash-appender:
      destination: 127.0.0.1:4560
    kafka-appender:
      bootstrap-servers: 127.0.0.1:9092
      topic: logback_to_logstash

console:
  user:
    username: seata  # 管控端登录用户名
    password: seata  # 管控端登录密码
seata:
  config:
    # support: nacos, consul, apollo, zk, etcd3
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848 # 本地nacos作为配置中心
      namespace: public
      group: SEATA_GROUP
      username:
      password:
      context-path:
      ##if use MSE Nacos with auth, mutex with username/password attribute
      #access-key:
      #secret-key:
      data-id: seataServer.properties # nacos配置中心上的信息
  registry:
    # support: nacos, eureka, redis, zk, consul, etcd3, sofa
    type: nacos
    nacos:  # 本地nacos作为注册中心
      application: seata-server
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      namespace: public
      cluster: default
      username:
      password:
      context-path:
      ##if use MSE Nacos with auth, mutex with username/password attribute
      #access-key:
      #secret-key:
  store:
    # support: file 、 db 、 redis 、 raft
    db:
      datasource: druid
      db-type: mysql
      driver-class-name: com.mysql.cj.jdbc.Driver # 数据库连接信息
      url: jdbc:mysql://127.0.0.1:3306/seata?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true
      user: root
      password: root
      min-conn: 10
      max-conn: 100
      global-table: global_table
      branch-table: branch_table
      lock-table: lock_table
      distributed-lock-table: distributed_lock
      query-limit: 1000
      max-wait: 5000
  #  server:
  #    service-port: 8091 #If not configured, the default is '${server.port} + 1000'
  security:
    secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
    tokenValidityInMilliseconds: 1800000
    ignore:
      urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.jpeg,/**/*.ico,/api/v1/auth/login,/metadata/v1/**

刚刚我们使用了nacos作为配置中心,因此我们需要到nacos上配置相关属性信息,访问 http://localhost:8848/nacos/ 页面,在配置列表菜单点击创建配置

在这里插入图片描述

注意Data IDGroup属性要和配置文件中保持一致,配置内容参考如下:

# 数据存储方式,db代表数据库
store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
## mysql 5.xx
## driverClassName = "com.mysql.jdbc.Driver"
## mysql 8.0
store.db.driverClassName=com.mysql.cj.jdbc.Driver
# 对应导入的数据库
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true&useSSL=false
store.db.user=root
store.db.password=root
store.db.minConn=5
store.db.maxConn=30
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.distributedLockTable=distributed_lock

# 对应 ${spring.application.name}-group,指定需要分布式事务的模块
service.vgroupMapping.seata-validate-demo-group=default
service.vgroupMapping.default=default
      
store.db.maxWait=5000
# 事务、日志等配置
server.recovery.committingRetryPeriod=1000
server.recovery.asynCommittingRetryPeriod=1000
server.recovery.rollbackingRetryPeriod=1000
server.recovery.timeoutRetryPeriod=1000
server.maxCommitRetryTimeout=-1
server.maxRollbackRetryTimeout=-1
server.rollbackRetryTimeoutUnlockEnable=false
server.undo.logSaveDays=7
server.undo.logDeletePeriod=86400000

# 客户端与服务端传输方式
transport.serialization=seata
transport.compressor=none
# 关闭metrics功能,提高性能
metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9898

完成以上配置以后,重新进入lib目录下,里面有一个jdbc目录,进入以后,根据需要使用的mysql数据库驱动版本,拷贝相应的jar包到lib目录下。

完成以上操作以后,重新进入bin目录,启动seata

seata-server.bat

启动完成以后,浏览器访问 http://localhost:7091 地址,如果出现登录页面,输入seata/seata 以后登录成功,则seata部署完成。

在这里插入图片描述

三、Spring cloud集成seata2.0 AT模式实战

在部署好seata 2.0以后,下面进行spring cloud集成seata2.0 AT模式实战环节。

首先,进行数据库环境准备,seata AT模式需要客户端应用创建undo_log表,建表语句如下:

-- seata_demo.undo_log definition

CREATE TABLE `undo_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `rollback_info` longblob NOT NULL,
  `log_status` int(11) NOT NULL,
  `log_created` datetime NOT NULL,
  `log_modified` datetime NOT NULL,
  `ext` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=63 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

创建好seata框架需要的表以后,我们新建一个业务表,参考SQL如下:

-- seata_demo.account definition

CREATE TABLE `account` (
  `name` varchar(100) NOT NULL,
  `account` decimal(10,0) DEFAULT NULL,
  PRIMARY KEY (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

INSERT INTO seata_demo.account(name, account) VALUES('A', 100);
INSERT INTO seata_demo.account(name, account) VALUES('B', 100);

数据库环境准备好以后,创建一个spring boot项目,我这里使用的spring boot版本是2.7.18,导入相关依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    <version>2021.0.5.0</version>
</dependency>

<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>2.0.0</version>
</dependency>

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.3.0</version>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.30</version>
</dependency>

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

编写AccountDO类:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class AccountDO {

    private String name;

    private double account;
}

编写Mapper层方法:

import com.seata.demo.vo.AccountDO;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Update;

@Mapper
public interface AccountMapper {

    @Insert("INSERT INTO account\n" +
            "(name, account)" +
            "VALUES(#{name}, #{account});")
    int insert(AccountDO accountVo);

    @Update("UPDATE account " +
            "SET account=account+#{account}" +
            "WHERE name=#{name};")
    int add (AccountDO accountVo);

}

编写Service层类:

import com.seata.demo.mapper.AccountMapper;
import com.seata.demo.vo.AccountDO;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class AccountService {

    @Autowired
    private AccountMapper accountMapper;

    @GlobalTransactional
    public int save(AccountDO accountVo) {
        return accountMapper.insert(accountVo);
    }

    public int add(AccountDO accountVo) {
        return accountMapper.add(accountVo);
    }

}

编写controller层类:

import com.seata.demo.service.AccountService;
import com.seata.demo.vo.AccountDO;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AccountController {

    @Autowired
    private AccountService accountService;

    @RequestMapping("/save")
    public int save(@RequestBody AccountDO accountVo) {
        return accountService.save(accountVo);
    }

    @RequestMapping("/transfer")
    @GlobalTransactional
    public boolean transfer(@RequestBody TransferVo transferVo) {
        // 转出方
        accountService.add(new AccountDO(transferVo.getFrom(), -1 * transferVo.getAccount()));
        // 转入方
        accountService.add(new AccountDO(transferVo.getTo(), transferVo.getAccount()));
        // int i = 1 / 0; // 人为制造异常
        return true;
    }
}

@Data
/**
 * 转账参数VO
 */
class TransferVo {
    private String from;

    private String to;

    double account;
}

编写application.yml配置文件:

server:
  port: 8888

spring:
  application:
    name: seata-validate-demo
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: root
    url: jdbc:mysql://127.0.0.1:3306/seata_demo

  cloud:
    nacos: # 需要依赖注册中心进行远程调用
      discovery:
        enabled: true
        namespace: public
      server-addr: 127.0.0.1:8848


seata:
  enabled: true
  application-id: seata-server
  # Seata 事务组编号,用于 TC 集群名
  tx-service-group: default
  service:
     vgroupMapping:
       default: default
       seata-validate-demo-group: default
  config:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848  # 对应nacos地址
      group: SEATA_GROUP
      data-id: seataServer.properties # 对应【分布式事务命名空间 ruoyi_seata】下创建ruoyiSeata.properties配置文件
      namespace:  # 对应分布式事务命名空间
  registry:
    type: nacos
    nacos:
      application: seata-server # 对应`conf/application.yml`中registry.nacos.application
      server-addr: 127.0.0.1:8848  # 对应nacos地址
      cluster: default
      group: SEATA_GROUP
      namespace:  # 对应分布式事务命名空间

项目整体结构参考如下:

在这里插入图片描述

完成编码和配置以后,启动项目,进行转账接口测试,请求之前:
在这里插入图片描述

进行请求:

在这里插入图片描述

此时查看应用后端,发现应用报错:

java.lang.ArrayIndexOutOfBoundsException: 0
	at io.seata.core.rpc.processor.client.ClientOnResponseProcessor.process(ClientOnResponseProcessor.java:103) ~[seata-all-2.0.0.jar:2.0.0]
	at io.seata.core.rpc.netty.AbstractNettyRemoting.processMessage(AbstractNettyRemoting.java:306) ~[seata-all-2.0.0.jar:2.0.0]
	at io.seata.core.rpc.netty.AbstractNettyRemotingClient$ClientHandler.channelRead(AbstractNettyRemotingClient.java:411) [seata-all-2.0.0.jar:2.0.0]

通过查看seata-server端,也发现报错日志:

21:55:25.432 ERROR --- [rverHandlerThread_1_6_500] [pc.netty.AbstractNettyRemoting] [bda$processMessage$2]  [192.168.239.1:8091:6161483039933980673] : 0104
==>
java.lang.NoClassDefFoundError: Could not initialize class io.seata.server.cluster.raft.RaftServerFactory$SingletonHandler
        at io.seata.server.cluster.raft.RaftServerFactory.getInstance(RaftServerFactory.java:78) ~[classes!/:2.0.0]
        at io.seata.server.session.GlobalSession.removeBranch(GlobalSession.java:352) ~[classes!/:2.0.0]
        at io.seata.server.session.SessionHelper.removeBranch(SessionHelper.java:429) ~[classes!/:2.0.0]
        at io.seata.server.coordinator.DefaultCore.lambda$doGlobalRollback$3(DefaultCore.java:325) ~[classes!/:2.0.0]

如果出现上面错误,需要在nacos配置中心上seata配置增加如下配置:

在这里插入图片描述

server.enableParallelRequestHandle=false

增加配置以后,重启seata和应用,重新请求:

在这里插入图片描述

请求完成以后:

在这里插入图片描述

可以看到此时转账成功了。

我们人为制造一下异常,查看一下异常情况下能不能正常回滚,修改代码:

@RequestMapping("/transfer")
    @GlobalTransactional
    public boolean transfer(@RequestBody TransferVo transferVo) {
        // 转出方
        accountService.add(new AccountDO(transferVo.getFrom(), -1 * transferVo.getAccount()));
        // 转入方
        accountService.add(new AccountDO(transferVo.getTo(), transferVo.getAccount()));
         int i = 1 / 0; // 人为制造异常
        return true;
    }

重启应用,进行请求:

在这里插入图片描述

查看数据库,数据没有发生变化:

在这里插入图片描述

可以看到,此时seata进行分布式事务管理是成功的,你也可以在方法执行完成之前进行断点,然后查看客户端undo_log,此时可以看到SQL镜像数据哦。

在这里插入图片描述

本次主要是介绍seata 2.0版本的AT模式入门,后续有时间会给大家介绍TCC模式、Saga模式以及seata的底层原理,和我们自定义的一些扩展,欢迎大家点点关注。

更多文章信息Spring Cloud与Seata 2.0集成:零基础安装到AT模式实战的完整教程

部署资源下载和完整demo参考下载地址:https://download.youkuaiyun.com/download/C_AJing/89693120?spm=1001.2014.3001.5503

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Coder-文小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值