3、分布式事务解决方案Seata初体验

本文基于SpringBoot+Dubbo+Nacos+Seata+MyBatis-Plus搭建分布式事务案例,模拟电商下单场景,包含扣减库存、账户余额和创建订单三个关键步骤。案例完整展示了分布式系统中如何保证多个服务操作的原子性,为处理跨服务事务提供实践参考。

1、案例搭建

1.1、业务说明

本案例演示的是下单的过程,具体流程包括:

  • 扣减库存
  • 扣减账户余额
  • 创建订单

我们知道这是一个典型的分布式场景,这三个环节要么同时成功,要么一起失败回滚

1.2、项目搭建

我的项目结构大致如下图所示,需要注意的是在这之上还有一个,所以要注意pom文件要注意一下

image-20251228102415197

1.2.1、seta-demo

seata-demo作为演示代码的父工程,下面包含狂起来的几个模块,pom文件如下:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.7.15</version>
            <scope>import</scope>
            <type>pom</type>
        </dependency>

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

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.6</version>
        </dependency>

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

        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>3.2.6</version>
        </dependency>

        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-nacos-spring-boot-starter</artifactId>
            <version>3.2.6</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.27</version>
        </dependency>
    </dependencies>
</dependencyManagement>

1.2.2、seata-mall

image-20251228103252697

1.2.2.1、pom文件版本依赖
<dependencies>
    <dependency>
        <groupId>com.study</groupId>
        <artifactId>mall-order-api</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>

    <dependency>
        <groupId>com.study</groupId>
        <artifactId>mall-user-api</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>

    <dependency>
        <groupId>com.study</groupId>
        <artifactId>mall-product-api</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>

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

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

    <dependency>
        <groupId>org.apache.dubbo</groupId>
        <artifactId>dubbo-spring-boot-starter</artifactId>
    </dependency>

    <dependency>
        <groupId>org.apache.dubbo</groupId>
        <artifactId>dubbo-nacos-spring-boot-starter</artifactId>
    </dependency>
</dependencies>
1.2.2.2、配置文件

application.properties

server.port=8080
spring.application.name=seata-mall

dubbo.application.name=seata-mall
dubbo.registry.address=nacos://${NACOS_ADDRESS}
dubbo.registry.parameters.username=${NACOS_USERNAME}
dubbo.registry.parameters.password=${NACOS_PASSWORD}
dubbo.consumer.check=false
dubbo.application.qos-port=22220
dubbo.protocol.name=tri
dubbo.protocol.port=50050
1.2.2.3、启动类

SeataMallBootstrap.java

@EnableDubbo
@SpringBootApplication
public class SeataMallBootstrap {

    public static void main(String[] args) {
        SpringApplication.run(SeataMallBootstrap.class, args);
    }
}
1.2.2.4、业务接口和实现类

IBusinessService.java

public interface IBusinessService {

    /**
     * 下单
     * @param userId    用户ID
     * @param productId 商品ID
     * @param number    数量
     * @return
     */
    String order(String userId, String productId, Integer number);
}

BusinessServiceImpl.java

@Slf4j
@Service
public class BusinessServiceImpl implements IBusinessService {

    @DubboReference
    private IUserService userService;

    @DubboReference
    private IProductService productService;

    @DubboReference
    private IOrderService orderService;

    @Override
    public String order(String userId, String productId, Integer number) {
        //获取单价
        Long price = this.productService.getPrice(productId);

        //扣减库存
        this.productService.detectStock(productId, number);

        Long amount = price * number;

        //扣减用户余额
        this.userService.deductMoney(userId, amount);

        //创建订单
        this.orderService.createOrder(userId, productId, number, amount);
        return "订单创建成功";
    }
}
1.2.2.5、接口请求对象

UserOrderRequest.java

@Data
public class UserOrderRequest implements Serializable {
    private String userId;
    private String productId;
    private Integer number;
    private Integer price;
}

1.2.2.6、接口

OrderController.java

@RestController
public class OrderController {

    @Resource
    private IBusinessService businessService;

    @PostMapping("/order")
    public String order(@RequestBody UserOrderRequest orderRequest){
        return this.businessService.order(
                orderRequest.getUserId(),
                orderRequest.getProductId(),
                orderRequest.getNumber()
        );
    }
}

1.2.3、seata-order

image-20251228103922246

1.2.3.1、mall-order-api

api比较简单,只有一个简单的接口定义IOrderService.java

public interface IOrderService {

    /**
     * 创建订单
     * @param userId    用户id
     * @param productId 商品id
     * @param number    数量
     * @param price    金额
     */
    void createOrder(String userId, String productId, Integer number, Long price);
}
1.2.3.2、mall-order-provider
1.2.3.2.1、定义订单实体和对应的mapper

Order.java

@Data
@TableName("mall_order")
public class Order implements java.io.Serializable {
    @TableId(type = IdType.AUTO)
    private Integer orderId;
    private String userId;
    private String productId;
    private Integer num;
    private Long amount;
}

OrderMapper.java

public interface OrderMapper extends BaseMapper<Order> {
}
1.2.3.2.2、接口实现类

OrderServiceImpl.java

@Slf4j
@DubboService
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {

    @Override
    public void createOrder(String userId, String productId, Integer number, Long amount) {
        log.info("createOrder: userId = {}, productId = {}, number = {}, amount = {}", userId , productId , number, amount);
        Order order = new Order();
        order.setUserId(userId);
        order.setProductId(productId);
        order.setNum(number);
        order.setAmount(amount);

        this.baseMapper.insert(order);
    }
}
1.2.3.2.3、启动类

SeataMallOrderBootstrap.java

@EnableDubbo
@SpringBootApplication
@MapperScan(basePackages = "com.study.mall.order")
public class SeataMallOrderBootstrap {

    public static void main(String[] args) {
        SpringApplication.run(SeataMallOrderBootstrap.class, args);
    }
}
1.2.3.2.4、配置文件

application.properties

server.port=8081
spring.application.name=mall-order-provider

spring.datasource.druid.initial-size=5
spring.datasource.druid.min-idle=5
spring.datasource.druid.max-active=50
spring.datasource.druid.max-wait=60000
spring.datasource.druid.validation-query=SELECT 1 FROM DUAL
spring.datasource.druid.filters=stat,wall
spring.datasource.druid.url=jdbc:mysql://${MYSQL_ADDRESS}/seata_mall_order?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false
spring.datasource.druid.username=${MYSQL_USERNAME}
spring.datasource.druid.password=${MYSQL_PASSWORD}


dubbo.application.name=mall-order-provider
dubbo.registry.address=nacos://${NACOS_ADDRESS}
dubbo.registry.register-mode=instance
dubbo.registry.parameters.username=${NACOS_USERNAME}
dubbo.registry.parameters.password=${NACOS_PASSWORD}
dubbo.application.qos-port=22221
dubbo.protocol.name=tri
dubbo.protocol.port=50051
1.2.3.2.5、pom版本依赖
<dependencies>
    <dependency>
        <groupId>com.study</groupId>
        <artifactId>mall-order-api</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>

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

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

    <dependency>
        <groupId>org.apache.dubbo</groupId>
        <artifactId>dubbo-spring-boot-starter</artifactId>
    </dependency>

    <dependency>
        <groupId>org.apache.dubbo</groupId>
        <artifactId>dubbo-nacos-spring-boot-starter</artifactId>
    </dependency>

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
    </dependency>

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>

1.2.4、seata-product

1.2.4.1、mall-product-api

api也是简单的一个接口定义IProductService.java

public interface IProductService {

    /**
     * 扣减库存
     * @param productId 商品id
     * @param number    扣减数量
     */
    void detectStock(String productId, Integer number);

    /**
     * 获取商品价格
     * @param productId 商品id
     * @return          商品价格
     */
    Long getPrice(String productId);
}
1.2.4.1、mall-product-provider
1.2.4.2.1、定义订单实体和对应的mapper

Product.java

@Data
@TableName("mall_product")
public class Product implements java.io.Serializable {

    @TableId(type = IdType.INPUT)
    private String productId;
	private String product_name;
    private Long price;
	private Integer stock;
}

ProductMapper.java

public interface ProductMapper extends BaseMapper<Product> {
}
1.2.4.2.2、接口实现类

ProductServiceImpl.java

@Slf4j
@DubboService
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements IProductService {

    @Override
    public void detectStock(String productId, Integer number) {
        log.info("detectStock: productId = {}, number = {}", productId, number);
        Product product = this.baseMapper.selectById(productId);
        if (Objects.isNull(product)) {
            throw new RuntimeException("商品不存在");
        }
        if (product.getStock() < number) {
            throw new RuntimeException("库存不足");
        }
        product.setStock(product.getStock() - number);
        this.baseMapper.updateById(product);
    }

    @Override
    public Long getPrice(String productId) {
        Product product = this.baseMapper.selectById(productId);
        if (Objects.isNull(product)) {
            throw new RuntimeException("商品不存在");
        }
        return product.getPrice();
    }
}
1.2.4.2.3、启动类
@EnableDubbo
@SpringBootApplication
@MapperScan(basePackages = "com.study.mall.product")
public class SeataMallProductBootstrap {

    public static void main(String[] args) {
        SpringApplication.run(SeataMallProductBootstrap.class, args);
    }
}
1.2.4.2.4、配置文件
server.port=8082
spring.application.name=mall-product-provider

spring.datasource.druid.initial-size=5
spring.datasource.druid.min-idle=5
spring.datasource.druid.max-active=50
spring.datasource.druid.max-wait=60000
spring.datasource.druid.validation-query=SELECT 1 FROM DUAL
spring.datasource.druid.filters=stat,wall
spring.datasource.druid.url=jdbc:mysql://${MYSQL_ADDRESS}/seata_mall_product?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false
spring.datasource.druid.username=${MYSQL_USERNAME}
spring.datasource.druid.password=${MYSQL_PASSWORD}


dubbo.application.name=mall-product-provider
dubbo.registry.address=nacos://${NACOS_ADDRESS}
dubbo.registry.register-mode=instance
dubbo.registry.parameters.username=${NACOS_USERNAME}
dubbo.registry.parameters.password=${NACOS_PASSWORD}
dubbo.application.qos-port=22222
dubbo.protocol.name=tri
dubbo.protocol.port=50052
1.2.4.2.5、pom版本依赖
<dependencies>
    <dependency>
        <groupId>com.study</groupId>
        <artifactId>mall-product-api</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>

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

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

    <dependency>
        <groupId>org.apache.dubbo</groupId>
        <artifactId>dubbo-spring-boot-starter</artifactId>
    </dependency>

    <dependency>
        <groupId>org.apache.dubbo</groupId>
        <artifactId>dubbo-nacos-spring-boot-starter</artifactId>
    </dependency>

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
    </dependency>

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>

1.2.5、seata-user

1.2.5.1、mall-user-api

api同样也只是定义一个接口

public interface IUserService {

    /**
     * 扣钱
     * @param userId
     * @param money
     */
    void deductMoney(String userId, Long money);
}
1.2.5.2、mall-user-provider
1.2.5.2.1、定义订单实体和对应的mapper

User.java

@Data
@TableName("mall_user")
public class User implements java.io.Serializable {

    @TableId(type = IdType.INPUT)
    private String userId;
    private String name;
    private Long money;
}

UserMapper.java

public interface UserMapper extends BaseMapper<User> {
}
1.2.5.2.2、接口实现类

UserServiceImpl.java

@Slf4j
@DubboService
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Override
    public void deductMoney(String userId, Long money) {
        log.info("deductMoney userId: {}, money: {}",  userId , money);

        User user = this.baseMapper.selectById(userId);
        if (Objects.isNull(user)) {
            throw new RuntimeException("用户不存在");
        }
        if (user.getMoney() < money) {
            throw new RuntimeException("余额不足");
        }
        user.setMoney(user.getMoney() - money);
        this.baseMapper.updateById(user);
    }
}
1.2.5.2.3、启动类
@EnableDubbo
@SpringBootApplication
@MapperScan(basePackages = "com.study.mall.user")
public class SeataMallUserBootstrap {

    public static void main(String[] args) {
        SpringApplication.run(SeataMallUserBootstrap.class, args);
    }
}
1.2.5.2.4、配置文件
server.port=8083
spring.application.name=mall-user-provider

spring.datasource.druid.initial-size=5
spring.datasource.druid.min-idle=5
spring.datasource.druid.max-active=50
spring.datasource.druid.max-wait=60000
spring.datasource.druid.validation-query=SELECT 1 FROM DUAL
spring.datasource.druid.filters=stat,wall
spring.datasource.druid.url=jdbc:mysql://${MYSQL_ADDRESS}/seata_mall_user?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false
spring.datasource.druid.username=${MYSQL_USERNAME}
spring.datasource.druid.password=${MYSQL_PASSWORD}


dubbo.application.name=mall-user-provider
dubbo.registry.address=nacos://${NACOS_ADDRESS}
dubbo.registry.register-mode=instance
dubbo.registry.parameters.username=${NACOS_USERNAME}
dubbo.registry.parameters.password=${NACOS_PASSWORD}
dubbo.application.qos-port=22223
dubbo.protocol.name=tri
dubbo.protocol.port=50053
1.2.5.2.5、pom版本依赖
<dependencies>
    <dependency>
        <groupId>com.study</groupId>
        <artifactId>mall-user-api</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>

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

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

    <dependency>
        <groupId>org.apache.dubbo</groupId>
        <artifactId>dubbo-spring-boot-starter</artifactId>
    </dependency>

    <dependency>
        <groupId>org.apache.dubbo</groupId>
        <artifactId>dubbo-nacos-spring-boot-starter</artifactId>
    </dependency>

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
    </dependency>

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>

1.3、数据库脚本

-- 创建数据库
CREATE DATABASE IF NOT EXISTS `seata_mall_user` CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_general_ci';
CREATE DATABASE IF NOT EXISTS `seata_mall_product` CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_general_ci';
CREATE DATABASE IF NOT EXISTS `seata_mall_order` CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_general_ci';

-- 创建表结构
CREATE TABLE IF NOT EXISTS `seata_mall_user`.`mall_user` (
  `user_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户ID',
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '用户名',
  `money` bigint DEFAULT '0' COMMENT '账户余额',
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='账户表';


CREATE TABLE IF NOT EXISTS `seata_mall_product`.`mall_product` (
  `product_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `product_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `price` bigint NOT NULL DEFAULT '0',
  `stock` int NOT NULL DEFAULT '0',
  PRIMARY KEY (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='商品表';


CREATE TABLE IF NOT EXISTS `seata_mall_order`.`mall_order` (
  `order_id` int NOT NULL AUTO_INCREMENT,
  `user_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '账户ID',
  `product_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '商品ID',
  `num` int NOT NULL COMMENT '购买商品数量',
  `amount` bigint NOT NULL COMMENT '订单总金额',
  PRIMARY KEY (`order_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='订单表';


-- 初始化
truncate table `seata_mall_user`.`mall_user` ;
INSERT INTO `seata_mall_user`.`mall_user` (`user_id`, `name`, `money`) VALUES ('10010', '张三', 100000);

truncate table `seata_mall_product`.`mall_product` ;
INSERT INTO `seata_mall_product`.`mall_product` (`product_id`, `product_name`, `price`, `stock`) VALUES ('A10021', 'iPhone 16 Pro Max', 9800, 100);

truncate table `seata_mall_order`.`mall_order` ;





-- 创建undo_log表
CREATE TABLE IF NOT EXISTS `seata_mall_user`. `undo_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(100) NOT NULL,
  `context` varchar(128) 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) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
truncate table `seata_mall_user`. `undo_log`;


CREATE TABLE IF NOT EXISTS `seata_mall_product`. `undo_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(100) NOT NULL,
  `context` varchar(128) 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) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
truncate table `seata_mall_product`. `undo_log`;


CREATE TABLE IF NOT EXISTS `seata_mall_order`. `undo_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(100) NOT NULL,
  `context` varchar(128) 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) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
truncate table `seata_mall_order`. `undo_log`;

1.4、正常执行

将mall-order-provider、mall-user-provider、mall-product-provider、seata-mall三个模块启动起来,登录到nacos控制台可以看到三个服务已经正常发布了

image-20251228143015252

数据库中的数据如下:

image-20251228143319538

image-20251228143337136

image-20251228143353621

接下来可以用postman模拟一下请求:

image-20251228143432336

可以看到请求成功,数据库中的数据也变成了这样:

image-20251228143527394

image-20251228143544511

image-20251228143620381

1.5、异常

接下来模拟一个异常,我们在OrderServiceImpl中模拟抛出一个异常后重启这几个服务,然后重置数据库中的数据后再用postman再次调用

@Slf4j
@DubboService
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {

    @Override
    public void createOrder(String userId, String productId, Integer number, Long amount) {
        log.info("createOrder: userId = {}, productId = {}, number = {}, amount = {}", userId , productId , number, amount);
        Order order = new Order();
        order.setUserId(userId);
        order.setProductId(productId);
        order.setNum(number);
        order.setAmount(amount);

        this.baseMapper.insert(order);

        throw new RuntimeException("测试创建订单异常");
    }
}

结果发现商品表里的库存被扣减了,账户的余额也被扣减了,但是订单表中并没有生成订单数据

很明显,这就是一个很严重的问题,如何解决呢?接下来就是Seata出场了

2、Seata接入(AT模式)

2.1、启动seata服务

部署方案参考open in new window

服务启动成功后登录控制台:默认账号和密码都是seata

image-20251228145428562

我这里什么都没修改,都是默认的配置直接启动的

2.2、应用接入

2.2.1、引入相关依赖

所有的分布式事务参与方都需要

<dependency>
    <groupId>org.apache.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
</dependency>

2.2.2、添加seata相关配置

这里以mall-product-provider为例,其它几个应用相同

# seata配置
seata.application-id=mall-product-provider
seata.tx-service-group=my_test_tx_group
seata.enabled=true
seata.service.vgroup-mapping.my_test_tx_group=default
seata.service.grouplist.default=${seata.address:192.168.0.110}:8091
seata.registry.type=file
seata.config.type=file
  • seata.application-id:应用标识

  • seata.tx-service-group:服务分组,应用会先找到seata.service.vgroup-mapping.服务分组名称对应的值,然后再从配置文件中找到seata.service.grouplist.这个值对应的内容找到seata集群地址

  • seata.registry.type:注册中心类型(后续会介绍)

  • seata.config.type:配置中心类型(后续会介绍)

2.2.3、通过注解声明全局分布式事务

在需要开启全局分布式事务的业务方法中打@GlobalTransactional,表示这里会开启分布式事务

@Slf4j
@Service
public class BusinessServiceImpl implements IBusinessService {

    @DubboReference
    private IUserService userService;

    @DubboReference
    private IProductService productService;

    @DubboReference
    private IOrderService orderService;

    @Override
    @GlobalTransactional
    public String order(String userId, String productId, Integer number) {
        //获取单价
        Long price = this.productService.getPrice(productId);

        //扣减库存
        this.productService.detectStock(productId, number);

        Long amount = price * number;

        //扣减用户余额
        this.userService.deductMoney(userId, amount);

        //创建订单
        this.orderService.createOrder(userId, productId, number, amount);
        return "订单创建成功";
    }
}

2.2.4、再次执行

上述配置完成后,再次执行下单方法,可以看到数据库中账号表里边的余额没有变化、商品表的库存数量也没有变化、订单表也没有生成订单,完全符合预期,查看业务日志可以看到事务的执行情况

  • seata-mall
2025-12-28 15:15:46.183  INFO 20760 --- [nio-8080-exec-4] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2025-12-28 15:15:46.183  INFO 20760 --- [nio-8080-exec-4] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2025-12-28 15:15:46.185  INFO 20760 --- [nio-8080-exec-4] o.s.web.servlet.DispatcherServlet        : Completed initialization in 1 ms
2025-12-28 15:15:46.219  INFO 20760 --- [nio-8080-exec-4] ServiceLoader$InnerEnhancedServiceLoader : Load compatible class io.seata.core.context.ContextCore
2025-12-28 15:15:46.278  INFO 20760 --- [nio-8080-exec-4] o.a.seata.tm.TransactionManagerHolder    : TransactionManager Singleton org.apache.seata.tm.DefaultTransactionManager@68fc95bf
2025-12-28 15:15:46.290  INFO 20760 --- [nio-8080-exec-4] o.a.s.tm.api.DefaultGlobalTransaction    : Begin new global transaction [172.26.0.1:8091:370025418433409035]
2025-12-28 15:15:50.993  INFO 20760 --- [nio-8080-exec-4] o.a.s.tm.api.DefaultGlobalTransaction    : transaction 172.26.0.1:8091:370025418433409035 will be rollback
2025-12-28 15:15:51.367  INFO 20760 --- [nio-8080-exec-4] o.a.s.tm.api.DefaultGlobalTransaction    : transaction end, xid = 172.26.0.1:8091:370025418433409035
2025-12-28 15:15:51.367  INFO 20760 --- [nio-8080-exec-4] o.a.s.tm.api.DefaultGlobalTransaction    : [172.26.0.1:8091:370025418433409035] rollback status: Rollbacked
2025-12-28 15:15:51.391 ERROR 20760 --- [nio-8080-exec-4] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: 测试创建订单异常] with root cause

java.lang.RuntimeException: 测试创建订单异常
	at com.study.mall.order.service.OrderServiceImpl.createOrder(OrderServiceImpl.java:25) ~[na:na]
	at com.study.mall.order.service.OrderServiceImpl$$FastClassBySpringCGLIB$$41f97068.invoke(<generated>) ~[na:na]
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.29.jar:5.3.29]
	at org.springframework.aop.framework.CglibAopProxy.invokeMethod(CglibAopProxy.java:386) ~[spring-aop-5.3.29.jar:5.3.29]
	at org.springframework.aop.framework.CglibAopProxy.access$000(CglibAopProxy.java:85) ~[spring-aop-5.3.29.jar:5.3.29]
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:704) ~[spring-aop-5.3.29.jar:5.3.29]
	at com.study.mall.order.service.OrderServiceImpl$$EnhancerBySpringCGLIB$$11a70ef9.createOrder(<generated>) ~[na:na]
	at com.study.mall.order.IOrderServiceDubboWrap0.invokeMethod(IOrderServiceDubboWrap0.java) ~[na:na]
	at org.apache.dubbo.rpc.proxy.javassist.JavassistProxyFactory$1.doInvoke(JavassistProxyFactory.java:73) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.proxy.AbstractProxyInvoker.invoke(AbstractProxyInvoker.java:100) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.config.invoker.DelegateProviderMetaDataInvoker.invoke(DelegateProviderMetaDataInvoker.java:55) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:56) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.filter.ClassLoaderCallbackFilter.invoke(ClassLoaderCallbackFilter.java:38) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:334) ~[dubbo-3.2.6.jar:3.2.6]
	at com.alibaba.dubbo.rpc.Invoker$CompatibleInvoker.invoke(Invoker.java:76) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.seata.integration.dubbo.alibaba.AlibabaDubboTransactionProviderFilter.invoke(AlibabaDubboTransactionProviderFilter.java:45) ~[seata-all-2.3.0.jar:2.3.0]
	at com.alibaba.dubbo.rpc.Filter.invoke(Filter.java:34) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:334) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.protocol.dubbo.filter.TraceFilter.invoke(TraceFilter.java:80) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:334) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.filter.TimeoutFilter.invoke(TimeoutFilter.java:45) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:334) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.seata.SeataTransactionPropagationProviderFilter.invoke(SeataTransactionPropagationProviderFilter.java:66) ~[dubbo-filter-seata-1.0.2.jar:1.0.2]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:334) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:108) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:334) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.filter.ExceptionFilter.invoke(ExceptionFilter.java:55) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:334) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.filter.AccessLogFilter.invoke(AccessLogFilter.java:119) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:334) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.filter.GenericFilter.invoke(GenericFilter.java:206) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:334) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.filter.ClassLoaderFilter.invoke(ClassLoaderFilter.java:54) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:334) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.filter.EchoFilter.invoke(EchoFilter.java:41) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:334) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.metrics.filter.MetricsFilter.invoke(MetricsFilter.java:73) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.metrics.filter.MetricsProviderFilter.invoke(MetricsProviderFilter.java:36) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:334) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.filter.ProfilerServerFilter.invoke(ProfilerServerFilter.java:64) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:334) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.filter.ContextFilter.invoke(ContextFilter.java:144) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:334) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CallbackRegistrationInvoker.invoke(FilterChainBuilder.java:196) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.protocol.tri.call.AbstractServerCallListener.invoke(AbstractServerCallListener.java:65) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.protocol.tri.call.UnaryServerCallListener.onComplete(UnaryServerCallListener.java:81) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.protocol.tri.call.AbstractServerCall.onComplete(AbstractServerCall.java:206) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.protocol.tri.stream.TripleServerStream$ServerDecoderListener.close(TripleServerStream.java:480) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.protocol.tri.frame.TriDecoder.deliver(TriDecoder.java:101) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.protocol.tri.frame.TriDecoder.close(TriDecoder.java:67) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.protocol.tri.stream.TripleServerStream$ServerTransportObserver.doOnData(TripleServerStream.java:443) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.protocol.tri.stream.TripleServerStream$ServerTransportObserver.lambda$onData$2(TripleServerStream.java:434) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.common.threadpool.serial.SerializingExecutor.run(SerializingExecutor.java:102) ~[dubbo-3.2.6.jar:3.2.6]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[na:1.8.0_421]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[na:1.8.0_421]
	at org.apache.dubbo.common.threadlocal.InternalRunnable.run(InternalRunnable.java:41) ~[dubbo-3.2.6.jar:3.2.6]
	at java.lang.Thread.run(Thread.java:750) [na:1.8.0_421]

2025-12-28 15:15:55.178  INFO 20760 --- [eoutChecker_2_1] o.a.s.c.r.n.NettyClientChannelManager    : will connect to 192.168.0.110:8091
2025-12-28 15:15:55.179  INFO 20760 --- [eoutChecker_2_1] o.a.s.c.rpc.netty.NettyPoolableFactory   : NettyPool create channel to transactionRole:RMROLE,address:192.168.0.110:8091,msg:< RegisterRMRequest{resourceIds='null', version='2.3.0', applicationId='seata-mall', transactionServiceGroup='my_test_tx_group', extraData='null'} >
2025-12-28 15:15:55.191  INFO 20760 --- [eoutChecker_2_1] o.a.s.c.rpc.netty.RmNettyRemotingClient  : register RM success. client version:2.3.0, server version:2.0.0,channel:[id: 0x9ed2077b, L:/192.168.0.102:58663 - R:/192.168.0.110:8091]
2025-12-28 15:15:55.191  INFO 20760 --- [eoutChecker_2_1] o.a.s.c.rpc.netty.NettyPoolableFactory   : register success, cost 7 ms, version:2.0.0,role:RMROLE,channel:[id: 0x9ed2077b, L:/192.168.0.102:58663 - R:/192.168.0.110:8091]
  • mall-order-provider
2025-12-28 15:15:49.705  INFO 24868 --- [:50051-thread-2] c.s.mall.order.service.OrderServiceImpl  : createOrder: userId = 10010, productId = A10021, number = 2, amount = 19600
2025-12-28 15:15:50.544  INFO 24868 --- [:50051-thread-2] ServiceLoader$InnerEnhancedServiceLoader : Load compatible class io.seata.rm.datasource.exec.InsertExecutor
2025-12-28 15:15:50.854  INFO 24868 --- [:50051-thread-2] o.a.seata.rm.AbstractResourceManager     : branch register success, xid:172.26.0.1:8091:370025418433409035, branchId:370025418433409038, lockKeys:mall_order:1
2025-12-28 15:15:50.868  WARN 24868 --- [:50051-thread-2] ServiceLoader$InnerEnhancedServiceLoader : Load [org.apache.seata.rm.datasource.undo.parser.ProtostuffUndoLogParser] class fail: io/protostuff/runtime/IdStrategy
2025-12-28 15:15:50.873  INFO 24868 --- [:50051-thread-2] ServiceLoader$InnerEnhancedServiceLoader : Load compatible class io.seata.rm.datasource.undo.parser.spi.JacksonSerializer
2025-12-28 15:15:50.940 ERROR 24868 --- [:50051-thread-2] o.a.dubbo.rpc.filter.ExceptionFilter     :  [DUBBO] Got unchecked and undeclared exception which called by 192.168.0.102. service: com.study.mall.order.IOrderService, method: createOrder, exception: java.lang.RuntimeException: 测试创建订单异常, dubbo version: 3.2.6, current host: 192.168.0.102, error code: 5-36. This may be caused by , go to https://dubbo.apache.org/faq/5/36 to find instructions. 

java.lang.RuntimeException: 测试创建订单异常
	at com.study.mall.order.service.OrderServiceImpl.createOrder(OrderServiceImpl.java:25) ~[classes/:na]
	at com.study.mall.order.service.OrderServiceImpl$$FastClassBySpringCGLIB$$41f97068.invoke(<generated>) ~[classes/:na]
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.29.jar:5.3.29]
	at org.springframework.aop.framework.CglibAopProxy.invokeMethod(CglibAopProxy.java:386) ~[spring-aop-5.3.29.jar:5.3.29]
	at org.springframework.aop.framework.CglibAopProxy.access$000(CglibAopProxy.java:85) ~[spring-aop-5.3.29.jar:5.3.29]
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:704) ~[spring-aop-5.3.29.jar:5.3.29]
	at com.study.mall.order.service.OrderServiceImpl$$EnhancerBySpringCGLIB$$11a70ef9.createOrder(<generated>) ~[classes/:na]
	at com.study.mall.order.IOrderServiceDubboWrap0.invokeMethod(IOrderServiceDubboWrap0.java) ~[dubbo-3.2.6.jar:na]
	at org.apache.dubbo.rpc.proxy.javassist.JavassistProxyFactory$1.doInvoke(JavassistProxyFactory.java:73) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.proxy.AbstractProxyInvoker.invoke(AbstractProxyInvoker.java:100) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.config.invoker.DelegateProviderMetaDataInvoker.invoke(DelegateProviderMetaDataInvoker.java:55) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:56) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.filter.ClassLoaderCallbackFilter.invoke(ClassLoaderCallbackFilter.java:38) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:334) ~[dubbo-3.2.6.jar:3.2.6]
	at com.alibaba.dubbo.rpc.Invoker$CompatibleInvoker.invoke(Invoker.java:76) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.seata.integration.dubbo.alibaba.AlibabaDubboTransactionProviderFilter.invoke(AlibabaDubboTransactionProviderFilter.java:45) ~[seata-all-2.3.0.jar:2.3.0]
	at com.alibaba.dubbo.rpc.Filter.invoke(Filter.java:34) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:334) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.protocol.dubbo.filter.TraceFilter.invoke(TraceFilter.java:80) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:334) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.filter.TimeoutFilter.invoke(TimeoutFilter.java:45) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:334) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.seata.SeataTransactionPropagationProviderFilter.invoke(SeataTransactionPropagationProviderFilter.java:66) ~[dubbo-filter-seata-1.0.2.jar:1.0.2]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:334) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:108) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:334) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.filter.ExceptionFilter.invoke(ExceptionFilter.java:55) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:334) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.filter.AccessLogFilter.invoke(AccessLogFilter.java:119) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:334) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.filter.GenericFilter.invoke(GenericFilter.java:206) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:334) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.filter.ClassLoaderFilter.invoke(ClassLoaderFilter.java:54) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:334) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.filter.EchoFilter.invoke(EchoFilter.java:41) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:334) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.metrics.filter.MetricsFilter.invoke(MetricsFilter.java:73) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.metrics.filter.MetricsProviderFilter.invoke(MetricsProviderFilter.java:36) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:334) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.filter.ProfilerServerFilter.invoke(ProfilerServerFilter.java:64) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:334) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.filter.ContextFilter.invoke(ContextFilter.java:144) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:334) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CallbackRegistrationInvoker.invoke(FilterChainBuilder.java:196) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.protocol.tri.call.AbstractServerCallListener.invoke(AbstractServerCallListener.java:65) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.protocol.tri.call.UnaryServerCallListener.onComplete(UnaryServerCallListener.java:81) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.protocol.tri.call.AbstractServerCall.onComplete(AbstractServerCall.java:206) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.protocol.tri.stream.TripleServerStream$ServerDecoderListener.close(TripleServerStream.java:480) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.protocol.tri.frame.TriDecoder.deliver(TriDecoder.java:101) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.protocol.tri.frame.TriDecoder.close(TriDecoder.java:67) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.protocol.tri.stream.TripleServerStream$ServerTransportObserver.doOnData(TripleServerStream.java:443) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.rpc.protocol.tri.stream.TripleServerStream$ServerTransportObserver.lambda$onData$2(TripleServerStream.java:434) ~[dubbo-3.2.6.jar:3.2.6]
	at org.apache.dubbo.common.threadpool.serial.SerializingExecutor.run(SerializingExecutor.java:102) ~[dubbo-3.2.6.jar:3.2.6]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[na:1.8.0_421]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[na:1.8.0_421]
	at org.apache.dubbo.common.threadlocal.InternalRunnable.run(InternalRunnable.java:41) ~[dubbo-3.2.6.jar:3.2.6]
	at java.lang.Thread.run(Thread.java:750) ~[na:1.8.0_421]

2025-12-28 15:15:51.246  INFO 24868 --- [h_RMROLE_1_1_16] o.a.s.c.r.p.c.RmBranchRollbackProcessor  : rm handle branch rollback process:BranchRollbackRequest{xid='172.26.0.1:8091:370025418433409035', branchId=370025418433409038, branchType=AT, resourceId='jdbc:mysql://192.168.0.110:3306/seata_mall_order', applicationData='null'}
2025-12-28 15:15:51.249  INFO 24868 --- [h_RMROLE_1_1_16] org.apache.seata.rm.AbstractRMHandler    : Branch Rollbacking: 172.26.0.1:8091:370025418433409035 370025418433409038 jdbc:mysql://192.168.0.110:3306/seata_mall_order
2025-12-28 15:15:51.356  INFO 24868 --- [h_RMROLE_1_1_16] o.a.s.r.d.undo.AbstractUndoLogManager    : xid 172.26.0.1:8091:370025418433409035 branch 370025418433409038, undo_log deleted with GlobalFinished
2025-12-28 15:15:51.357  INFO 24868 --- [h_RMROLE_1_1_16] o.a.s.rm.datasource.DataSourceManager    : branch rollback success, xid:172.26.0.1:8091:370025418433409035, branchId:370025418433409038
2025-12-28 15:15:51.358  INFO 24868 --- [h_RMROLE_1_1_16] org.apache.seata.rm.AbstractRMHandler    : Branch Rollbacked result: PhaseTwo_Rollbacked

  • mall-user-provider
2025-12-28 15:15:48.192  INFO 29260 --- [:50053-thread-2] c.s.mall.user.service.UserServiceImpl    : deductMoney userId: 10010, money: 19600
2025-12-28 15:15:49.095  INFO 29260 --- [:50053-thread-2] o.a.seata.rm.AbstractResourceManager     : branch register success, xid:172.26.0.1:8091:370025418433409035, branchId:370025418433409037, lockKeys:mall_user:10010
2025-12-28 15:15:49.109  WARN 29260 --- [:50053-thread-2] ServiceLoader$InnerEnhancedServiceLoader : Load [org.apache.seata.rm.datasource.undo.parser.ProtostuffUndoLogParser] class fail: io/protostuff/runtime/IdStrategy
2025-12-28 15:15:49.115  INFO 29260 --- [:50053-thread-2] ServiceLoader$InnerEnhancedServiceLoader : Load compatible class io.seata.rm.datasource.undo.parser.spi.JacksonSerializer
2025-12-28 15:15:51.124  INFO 29260 --- [h_RMROLE_1_1_16] o.a.s.c.r.p.c.RmBranchRollbackProcessor  : rm handle branch rollback process:BranchRollbackRequest{xid='172.26.0.1:8091:370025418433409035', branchId=370025418433409037, branchType=AT, resourceId='jdbc:mysql://192.168.0.110:3306/seata_mall_user', applicationData='null'}
2025-12-28 15:15:51.126  INFO 29260 --- [h_RMROLE_1_1_16] org.apache.seata.rm.AbstractRMHandler    : Branch Rollbacking: 172.26.0.1:8091:370025418433409035 370025418433409037 jdbc:mysql://192.168.0.110:3306/seata_mall_user
2025-12-28 15:15:51.235  INFO 29260 --- [h_RMROLE_1_1_16] o.a.s.r.d.undo.AbstractUndoLogManager    : xid 172.26.0.1:8091:370025418433409035 branch 370025418433409037, undo_log deleted with GlobalFinished
2025-12-28 15:15:51.236  INFO 29260 --- [h_RMROLE_1_1_16] o.a.s.rm.datasource.DataSourceManager    : branch rollback success, xid:172.26.0.1:8091:370025418433409035, branchId:370025418433409037
2025-12-28 15:15:51.237  INFO 29260 --- [h_RMROLE_1_1_16] org.apache.seata.rm.AbstractRMHandler    : Branch Rollbacked result: PhaseTwo_Rollbacked

  • mall-product_provider
2025-12-28 15:15:47.227  INFO 33244 --- [:50052-thread-3] c.s.m.p.service.ProductServiceImpl       : detectStock: productId = A10021, number = 2
2025-12-28 15:15:47.668  INFO 33244 --- [:50052-thread-3] o.a.seata.rm.AbstractResourceManager     : branch register success, xid:172.26.0.1:8091:370025418433409035, branchId:370025418433409036, lockKeys:mall_product:A10021
2025-12-28 15:15:47.682  WARN 33244 --- [:50052-thread-3] ServiceLoader$InnerEnhancedServiceLoader : Load [org.apache.seata.rm.datasource.undo.parser.ProtostuffUndoLogParser] class fail: io/protostuff/runtime/IdStrategy
2025-12-28 15:15:47.687  INFO 33244 --- [:50052-thread-3] ServiceLoader$InnerEnhancedServiceLoader : Load compatible class io.seata.rm.datasource.undo.parser.spi.JacksonSerializer
2025-12-28 15:15:51.004  INFO 33244 --- [h_RMROLE_1_1_16] o.a.s.c.r.p.c.RmBranchRollbackProcessor  : rm handle branch rollback process:BranchRollbackRequest{xid='172.26.0.1:8091:370025418433409035', branchId=370025418433409036, branchType=AT, resourceId='jdbc:mysql://192.168.0.110:3306/seata_mall_product', applicationData='null'}
2025-12-28 15:15:51.005  INFO 33244 --- [h_RMROLE_1_1_16] org.apache.seata.rm.AbstractRMHandler    : Branch Rollbacking: 172.26.0.1:8091:370025418433409035 370025418433409036 jdbc:mysql://192.168.0.110:3306/seata_mall_product
2025-12-28 15:15:51.111  INFO 33244 --- [h_RMROLE_1_1_16] o.a.s.r.d.undo.AbstractUndoLogManager    : xid 172.26.0.1:8091:370025418433409035 branch 370025418433409036, undo_log deleted with GlobalFinished
2025-12-28 15:15:51.112  INFO 33244 --- [h_RMROLE_1_1_16] o.a.s.rm.datasource.DataSourceManager    : branch rollback success, xid:172.26.0.1:8091:370025418433409035, branchId:370025418433409036
2025-12-28 15:15:51.114  INFO 33244 --- [h_RMROLE_1_1_16] org.apache.seata.rm.AbstractRMHandler    : Branch Rollbacked result: PhaseTwo_Rollbacked

总结:

seata作为阿里巴巴开源的一个分布式事务的解决方案,提供了四种事务模式,AT模式(默认)、TCC模式、Saga模式以及XA模式。而且AT模式是代码无侵入的,只需要通过简单的配置和一个事务注解即可完成,使用起来非常简单。至于里边的原理后续文章再介绍


扫码关注

最后更新时间: 2026/1/9 14:14:44