Spring Boot集成Seata实现微服务分布式事务实战方案

微服务架构中,跨服务的业务操作需要分布式事务保障数据一致性。Spring Boot框架在单体应用中依赖本地事务即可满足需求,但拆分为微服务后,订单服务、库存服务、账户服务各自维护独立数据库,本地事务无法覆盖跨库操作。Seata作为阿里开源的分布式事务解决方案,通过AT模式实现了对业务代码零侵入的分布式事务管理,是后端开发中服务治理的重要组件。

Seata分布式事务架构与AT模式原理

Seata的AT(Automatic Transaction)模式包含三个核心角色:TC(Transaction Coordinator)事务协调者、TM(Transaction Manager)事务管理器、RM(Resource Manager)资源管理器。TC是独立部署的服务端,TM和RM是嵌入应用中的客户端。

AT模式的工作流程分为两个阶段。第一阶段,各分支事务执行业务SQL并提交本地事务,Seata自动记录数据快照(before image和after image)到undo_log表,同时向TC注册分支事务。第二阶段,若全局事务决议提交,TC异步删除undo_log;若决议回滚,TC通知各RM根据undo_log生成反向SQL恢复数据。

这种设计使得业务代码无需感知分布式事务的存在,只需通过@GlobalTransactional注解标注全局事务入口即可。消息中间件在最终一致性场景中可以与Seata形成互补。

Seata服务端部署与Nacos注册中心配置

Seata Server(TC)部署使用Docker方式,基于Nacos作为注册中心和配置中心:

# docker-compose.yml
version: '3.8'
services:
  seata-server:
    image: seataio/seata-server:2.0.0
    container_name: seata-server
    ports:
      - "8091:8091"
      - "7091:7091"
    environment:
      - SEATA_PORT=8091
      - STORE_MODE=db
      - SEATA_IP=192.168.1.100
    volumes:
      - ./seata-config/application.yml:/seata-server/resources/application.yml
    depends_on:
      - nacos
      - seata-mysql

  seata-mysql:
    image: mysql:8.0
    container_name: seata-mysql
    environment:
      MYSQL_ROOT_PASSWORD: seata123
      MYSQL_DATABASE: seata
    ports:
      - "3307:3306"
    volumes:
      - ./seata-mysql-init:/docker-entrypoint-initdb.d

Seata Server的application.yml配置数据库存储模式:

seata:
  config:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      namespace: seata
  registry:
    type: nacos
    nacos:
      application: seata-server
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      namespace: seata
  store:
    mode: db
    db:
      datasource: druid
      db-type: mysql
      url: jdbc:mysql://seata-mysql:3306/seata?useUnicode=true
      user: root
      password: seata123
      min-conn: 5
      max-conn: 30
      global-table: global_table
      branch-table: branch_table
      lock-table: lock_table
      query-limit: 100

微服务客户端集成与数据源代理配置

订单服务、库存服务、账户服务三个微服务都需要集成Seata客户端。以订单服务为例,Maven依赖配置:

<dependencies>
    <!-- Seata Spring Boot Starter -->
    <dependency>
        <groupId>io.seata</groupId>
        <artifactId>seata-spring-boot-starter</artifactId>
        <version>2.0.0</version>
    </dependency>

    <!-- Seata与Nacos集成 -->
    <dependency>
        <groupId>com.alibaba.nacos</groupId>
        <artifactId>nacos-client</artifactId>
        <version>2.3.0</version>
    </dependency>
</dependencies>

application.yml配置Seata客户端:

seata:
  enabled: true
  application-id: order-service
  tx-service-group: default_tx_group
  service:
    vgroup-mapping:
      default_tx_group: default
    grouplist:
      default: 192.168.1.100:8091
  registry:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      namespace: seata
  datasource:
    proxy-enabled: true
  data-source-proxy-mode: AT

每个业务数据库需要创建undo_log表,Seata通过该表记录数据变更前后的快照:

-- 在每个业务数据库中执行
CREATE TABLE `undo_log` (
    `branch_id` BIGINT NOT NULL COMMENT '分支事务ID',
    `xid` VARCHAR(128) NOT NULL COMMENT '全局事务ID',
    `context` VARCHAR(128) NOT NULL COMMENT '上下文',
    `rollback_info` LONGBLOB NOT NULL COMMENT '回滚信息',
    `log_status` INT NOT NULL COMMENT '状态',
    `log_created` DATETIME NOT NULL COMMENT '创建时间',
    `log_modified` DATETIME NOT NULL COMMENT '修改时间',
    `ext` VARCHAR(100) DEFAULT NULL COMMENT '扩展信息',
    PRIMARY KEY (`branch_id`),
    KEY `idx_log_created` (`log_created`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Seata AT模式undo_log表';

高并发场景下的分布式事务实战编码

以电商下单场景为例,订单服务创建订单后调用库存服务和账户服务。使用@GlobalTransactional注解开启全局事务:

@Service
@Slf4j
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private StorageFeignClient storageFeignClient;

    @Autowired
    private AccountFeignClient accountFeignClient;

    @GlobalTransactional(name = "createOrder", timeoutMills = 60000,
                         rollbackFor = Exception.class)
    public Order createOrder(OrderDTO dto) {
        log.info("====== 开始创建订单, XID: {} ======",
            RootContext.getXID());

        // 1. 创建订单(本地事务)
        Order order = new Order();
        order.setUserId(dto.getUserId());
        order.setProductId(dto.getProductId());
        order.setCount(dto.getCount());
        order.setMoney(dto.getMoney());
        order.setStatus(0); // 待支付
        orderMapper.insert(order);
        log.info("订单创建成功, orderId={}", order.getId());

        // 2. 扣减库存(远程调用)
        storageFeignClient.deduct(dto.getProductId(), dto.getCount());
        log.info("库存扣减成功");

        // 3. 扣减账户余额(远程调用)
        accountFeignClient.debit(dto.getUserId(), dto.getMoney());
        log.info("账户余额扣减成功");

        // 4. 修改订单状态
        order.setStatus(1); // 已支付
        orderMapper.updateById(order);
        log.info("订单状态更新成功");

        return order;
    }
}

库存服务和账户服务通过Feign调用,被调方不需要添加@GlobalTransactional,Seata通过RPC header自动传递XID。库存服务代码:

@RestController
@RequestMapping("/storage")
public class StorageController {

    @Autowired
    private StorageService storageService;

    @PostMapping("/deduct")
    public Result<Void> deduct(@RequestParam Long productId,
                                  @RequestParam Integer count) {
        storageService.deduct(productId, count);
        return Result.success();
    }
}

@Service
public class StorageService {

    @Autowired
    private StorageMapper storageMapper;

    @Transactional(rollbackFor = Exception.class)
    public void deduct(Long productId, Integer count) {
        Storage storage = storageMapper.selectByProductId(productId);
        if (storage == null) {
            throw new RuntimeException("商品不存在");
        }
        if (storage.getResidue() < count) {
            throw new RuntimeException("库存不足");
        }
        storage.setUsed(storage.getUsed() + count);
        storage.setResidue(storage.getResidue() - count);
        storageMapper.updateById(storage);
    }
}

当账户余额不足抛出异常时,全局事务回滚,Seata根据undo_log自动恢复订单状态和库存数据。API接口规范方面,建议在Feign接口中统一使用Result包装类,便于异常信息的传递和解析。

分布式事务监控与服务治理实践

Seata提供事务日志查询接口,可通过控制台监控全局事务执行情况。高并发设计需要关注几个配置项:

# Seata客户端高并发优化配置
seata:
  client:
    rm:
      lock:
        retry-interval: 30
        retry-times: 10
    tm:
      commit-retry-count: 5
      rollback-retry-count: 5
      default-global-transaction-timeout: 60000
  transport:
    type: TCP
    server: NIO
    heartbeat: true
    enable-client-batch-send-request: true

业务中台建设时,Seata全局锁可能成为性能瓶颈。对于读多写少的场景,可通过@GlobalLock + SELECT FOR UPDATE替代AT模式,减少undo_log的写入开销。服务治理层面,业务中台建设应将Seata事务超时时间与下游服务超时配置对齐,避免全局事务超时回滚但分支事务仍在执行的竞态问题。

生产环境部署建议:TC集群至少3节点保证高可用,使用MySQL存储模式替代文件模式避免单点故障,定期清理已完成事务的undo_log和global_table记录防止表膨胀。通过Nacos动态配置调整重试策略,实现不停机的事务参数调优。微服务架构中,Seata与Spring Boot框架的集成需要确保数据源代理正确生效,当使用Druid等连接池时,需检查代理顺序是否覆盖了所有事务操作。Java/Go实战中,Seata也提供Go语言客户端,支持跨语言微服务架构的分布式事务协调。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/springboot-ji-cheng-seata-shi-xian-wei-fu-wu-fen-bu-shi-shi/

(0)
小编小编
上一篇 10小时前
下一篇 10小时前

相关推荐