一、seata介绍

1.seata安装

seata安装的安装,可以参考另外一篇博客

https://blog.csdn.net/zxd1435513775/article/details/121871487

2.seata简介

seata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。

seata官方网址:https://seata.io/zh-cn/

3. 分布式架构

在分布式之前,单机单库或者,多个微服务连接同一个数据库,事务的一致性可以得到解决,使用@Transactional注解就好,此注解的事务是面对的同一个数据源。

但在分布式之后,单体应用被拆分成多个微服务应用,每个微服务应用都连接各自独立的数据库,分别使用自己独立的数据源,此时每个微服务内部的数据一致性由本地事务来保证,但是全局的数据一致性问题没法保证。

在这里插入图片描述

一次业务操作需要跨多个数据源或需要跨多个系统进行远程调用,就会产生分布式事务问题

4.seata的过程ID和三组件模型

Transaction ID XID:全局唯一的事务ID

三组件模型

  • Transaction Coordinator(TC) : 事务协调者,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚
  • Transaction Manager(TM): 事务管理器,控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议
  • Resource Manager(RM) :资源管理器,控制分支事务,负责分支注册,状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚

5. seata处理事务过程

    1. TMTC申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的XID
    1. XID在微服务调用链路的上下文中传播
    1. RMTC注册分支事务,将其纳入XID对应全局事务的管辖
    1. TMTC发起针对XID的全局提交或回滚决议
    1. TC调度XID下管辖的全部分支事务完成提交或回滚请求

在这里插入图片描述

二、seata集成示例

1.案例分析

模拟场景,创建订单、修改库存数量、扣减用户余额,分别对应三个微服务:

  • seata-order-service 订单服务
  • seata-storage-service 库存服务
  • seata-account-service 账户服务

三个服务的调用顺序如下:

订单服务创建订单->调用库存服务扣减库存->调用账户服务扣减账户余额->订单服务修改订单状态

文章末尾提供示例源码,包括mysql初始化脚本,但数据库得自己创建

分别创建三个数据库,与三个服务对应:seata-order、seata-storage、seata-account

创建完三个数据库后,要执行seata的脚本:

github地址:https://github.com/seata/seata/blob/develop/script/client/at/db/mysql.sql

CREATE TABLE IF NOT EXISTS `undo_log`
(
    `branch_id`     BIGINT       NOT NULL COMMENT 'branch transaction id',
    `xid`           VARCHAR(128) 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';

另外要把另外三个库的初始化脚本也执行完,最后数据库结构如下:

每个服务的数据库都要创建一个 undo_log 回滚日志表

在这里插入图片描述

三个数据库数据的初始状态如下:

订单库没有订单

在这里插入图片描述

库存库有100件库存

在这里插入图片描述

账户库余额有1000

在这里插入图片描述

如果三个服务之间,没有发生异常,程序也没有集成seata,程序正常调用,在浏览器中输入如下地址,用get方式容易测试。正常情况下,没有问题。

http://localhost:2001/order/create?userId=1&productId=1&count=10&money=100

订单创建成功,支付状态修改成功

在这里插入图片描述

库存扣减成功

在这里插入图片描述

账户余额扣减成功

在这里插入图片描述

现在,要模拟程序发送网络异常或者程序异常的情况,在seata-account-service中,让线程阻塞,同时设置openFeign调用超时时间为5秒

# 设置openFeign超时时间
feign.client.config.default.connect-timeout=5000
feign.client.config.default.read-timeout=5000
public void decrease(Long userId, BigDecimal money) {

    log.info("------->account-service中扣减账户余额开始");
    // 模拟超时
    try {
        TimeUnit.SECONDS.sleep(20);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    accountDao.decrease(userId,money);
    log.info("------->account-service中扣减账户余额结束");
}

然后,再次访问刚才的地址:

http://localhost:2001/order/create?userId=1&productId=1&count=10&money=100

会发现,seata-order-service服务报了超时异常,但订单已经创建成功,并且库存量和账户余额都被扣减,但订单的状态仍然是未支付状态,这样是不对的!!!!

在这里插入图片描述

订单创建成功,但支付状态为:未支付。0表示未支付,1表示已支付。

在这里插入图片描述

库存扣减成功

在这里插入图片描述

账户余额扣减成功,但订单的状态仍为:未支付!!!

在这里插入图片描述

2.seata管理分布式事务

下面就需要用seata来管理分布式事务。在做下面事情之前,要启动NacosSeata服务!!!

1.引依赖

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    <version>2021.1</version>
    <exclusions>
        <exclusion>
            <groupId>io.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>1.4.2</version>
</dependency>

2.改配置文件

每个微服务的配置文件都要加seata配置!!!

# seata配置
seata.enabled=true
seata.enable-auto-data-source-proxy=true
# config.txt文件中修改的vgroupMapping值
seata.tx-service-group=scorpios_tx_group 
seata.registry.type=nacos
# registry.conf配置文件中 nacos的application名称
seata.registry.nacos.application=seata-server
seata.registry.nacos.server-addr=localhost:8848
seata.registry.nacos.username=nacos
seata.registry.nacos.password=nacos
seata.registry.nacos.namespace=88b8f583-43f9-4272-bd46-78a9f89c56e8
seata.registry.nacos.cluster=default
seata.registry.nacos.group=SEATA_GROUP

seata.config.type=nacos
seata.config.nacos.server-addr=localhost:8848
seata.config.nacos.namespace=88b8f583-43f9-4272-bd46-78a9f89c56e8
seata.config.nacos.group=SEATA_GROUP
seata.config.nacos.username=nacos
seata.config.nacos.password=nacos

3.开启注解

每个微服务都要开启seata!!!

@SpringBootApplication
@MapperScan({"com.scorpios.order.dao"})
@EnableDiscoveryClient
@EnableFeignClients(basePackages = "com.scorpios")
@EnableAutoDataSourceProxy	// 开启seata
public class SeataOrderServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(SeataOrderServiceApplication.class, args);
    }

}

4.使用注解

seata-order-service的业务方法上,添加seata分布式事务注解:@GlobalTransactional

// 使用seata的注解,支持定制化,只对哪些异常进行处理
@GlobalTransactional(name = "scorpios-create-order",rollbackFor = Exception.class)
@Override
public void create(Order order){
    log.info("----->开始新建订单");
    //新建订单
    orderDao.create(order);

    //扣减库存
    log.info("----->订单微服务开始调用库存,做扣减Count");
    storageService.decrease(order.getProductId(),order.getCount());
    log.info("----->订单微服务开始调用库存,做扣减end");

    //扣减账户
    log.info("----->订单微服务开始调用账户,做扣减Money");
    accountService.decrease(order.getUserId(),order.getMoney());
    log.info("----->订单微服务开始调用账户,做扣减end");

    //修改订单状态,从零到1代表已经完成
    log.info("----->修改订单状态开始");
    orderDao.update(order.getUserId(),0);
    log.info("----->修改订单状态结束");

    log.info("----->下订单结束了");
}

5.测试结果

再次输入如下地址:

http://localhost:2001/order/create?userId=1&productId=1&count=10&money=100

会发现seata-order-service会报超时异常,同时seata已经完成数据回滚

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

再去观察三个数据库,发现数据并没有任何变化,此处就不贴图了,seata集成完毕。

一句话总结,就是一个注解@GlobalTransactional的使用,哈哈哈~

项目源码地址:https://github.com/Hofanking/springboot-seata-example

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐