Seata实战

案例分析

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

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

        三个服务的调用顺序如下:订单服务创建订单->调用库存服务扣减库存->调用账户服务扣减账户余额->订单服务修改订单状态

       在AT模式中,需要在参与全局事务的数据库中,添加一个undo_log表,每个服务的数据库都要创建一个 undo_log 回滚日志表

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 = utf8mb4 COMMENT ='AT transaction mode undo table';

   在全局事务的一阶段中,分支事务在获取到全局锁提交事务时,会释放本地锁和连接资源,并在undo_log表中插入一条数据。重要的是rollback_info,比如在更新一条数据时set money = 97,会查询修改之前该条数据的及修改后的数据状态

  在第二阶段中,如果全局事务成功,会收到 TC 的分支提交请求,把请求放入一个异步任务的队列中,马上返回提交成功的结果给 TC。异步任务阶段的分支提交请求将异步和批量地删除相应 UNDO LOG 记录。

  在第二阶段中,如果全局事务失败,会收到 TC 的分支回滚请求,开启一个本地事务,执行如下操作。

  通过 XID 和 Branch ID 查找到相应的 UNDO LOG 记录。
  数据校验:拿 UNDO LOG 中的后镜与当前数据进行比较,如果有不同,说明数据被当前全局事务之外的动作做了修改。这种情况,需要根据配置策略来做处理,详细的说明在另外的文档中介绍。
  根据 UNDO LOG 中的前镜像和业务 SQL 的相关信息生成并执行回滚的语句。
  提交本地事务。并把本地事务的执行结果(即分支事务回滚的结果)上报给 TC。

      t_account表,账户余额有1000

CREATE TABLE t_account(
  `id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'id',
  `user_id` BIGINT(11) DEFAULT NULL COMMENT '用户id',
  `total` DECIMAL(10,0) DEFAULT NULL COMMENT '总额度',
  `used` DECIMAL(10,0) DEFAULT NULL COMMENT '已用余额',
  `residue` DECIMAL(10,0) DEFAULT '0' COMMENT '剩余可用额度'
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

INSERT INTO seata_account.t_account(`id`,`user_id`,`total`,`used`,`residue`) VALUES('1','1','1000','0','1000')

       t_order表

CREATE TABLE t_order(
    `id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
    `user_id` BIGINT(11) DEFAULT NULL COMMENT '用户id',
    `product_id` BIGINT(11) DEFAULT NULL COMMENT '产品id',
    `count` INT(11) DEFAULT NULL COMMENT '数量',
    `money` DECIMAL(11,0) DEFAULT NULL COMMENT '金额',
    `status` INT(1) DEFAULT NULL COMMENT '订单状态:0:创建中; 1:已完结'
) ENGINE=INNODB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

       t_storage表,库存有100

CREATE TABLE t_storage(
  `id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
  `product_id` BIGINT(11) DEFAULT NULL COMMENT '产品id',
  `total` INT(11) DEFAULT NULL COMMENT '总库存',
  `used` INT(11) DEFAULT NULL COMMENT '已用库存',
  `residue` INT(11) DEFAULT NULL COMMENT '剩余库存'
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

INSERT INTO seata_storage.t_storage(`id`,`product_id`,`total`,`used`,`residue`)
VALUES('1','1','100','0','100');

       问题:创建订单->扣减库存->扣减账户完成,由于超时,订单状态没有更新

seata分布式事务

        依赖  

        <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.1</version>
        </dependency>

        配置文件

# seata配置
seata.enabled=true
seata.enable-auto-data-source-proxy=true
# config.txt文件中修改的vgroupMapping值
seata.tx-service-group=default
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

      开启注解

@EnableAutoDataSourceProxy

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

    @GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
    public void create(Order order) {
...

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

参考:

       https://www.cnblogs.com/jian0110/p/14762509.html

       https://blog.csdn.net/hosaos/article/details/89136666

 
posted on 2022-04-09 15:45  溪水静幽  阅读(169)  评论(0)    收藏  举报