Spring Boot 与 Seata:深入理解与实践四大分布式事务模式

概述:

在现代微服务架构中,处理跨服务的数据一致性是一个复杂且挑战性的问题。随着服务拆分的细化,传统的事务管理机制不再适用,这就需要一种新的解决方案来确保分布式系统中事务的一致性。Seata,作为一种流行的分布式事务协调器,提供了四种不同的事务模式来应对各种复杂场景:AT、TCC、SAGA 和 XA。本文将带你深入理解这四种模式,并展示如何在 Spring Boot 应用中集成和实践 Seata,从而解锁微服务事务管理的新纪元。

Seata 支持四种事务模式:AT(自动补偿)、TCC(Try-Confirm-Cancel)、SAGA 和 XA。每种模式都有其适用场景和使用方式。 在 Spring Boot 集成 Seata 之后,你可以根据业务需求选择适当的模式,并进行相应的配置和编码以更改和使用这些模式。

AT 模式(默认模式)

AT 模式是 Seata 默认的事务模式,它是基于二阶段提交的自动补偿模式。在 Spring Boot 中,通常不需要做额外配置即可使用 AT 模式,因为它是通过代理数据源自动实现的。

Seata 的 AT(Automatic Transaction) 模式是默认的分布式事务模式,它基于两阶段提交协议(2PC)的变种实现。AT 模式通过代理数据源自动拦截 SQL,记录数据变更的前后镜像,以实现分布式事务的提交和回滚。该模式适合于对一致性要求不是特别高的业务场景,因为它在性能和简单性上相对更优。### 集成 AT 模式

  1. 添加 Seata 依赖 : 在 Spring Boot 项目的 pom.xml 文件中添加 Seata Starter 依赖:

    xml 复制代码
    <dependency>
        <groupId>io.seata</groupId>
        <artifactId>seata-spring-boot-starter</artifactId>
        <version>版本号</version>
    </dependency>
  2. 配置 Seata : 在 application.yml 中配置 Seata 相关属性,包括服务分组、服务名、注册中心和配置中心的配置:

    yaml 复制代码
    seata:
      enabled: true
      application-id: ${spring.application.name}
      tx-service-group: my_tx_group
      client:
        rm:
          report-success-enable: false
          table-meta-check-enable: false
        tm:
          commit-retry-count: 5
          rollback-retry-count: 5
      service:
        vgroup-mapping:
          my_tx_group: default
      registry:
        type: nacos
        nacos:
          server-addr: 127.0.0.1:8848
      config:
        type: nacos
        nacos:
          server-addr: 127.0.0.1:8848
yaml 复制代码
seata:
 client:
   rm:
     datasource:
       datasource-type: AT
  1. 配置数据源代理: Seata AT 模式需要代理数据源以拦截 SQL。在 Spring Boot 中,可以通过配置类来实现:

    java 复制代码
    @Configuration
    public class DataSourceProxyConfig {
    
        @Bean @ConfigurationProperties(prefix = "spring.datasource")
        public DataSource dataSource(DataSourceProperties properties) {
            // 配置真实数据源
            DataSource dataSource = properties.initializeDataSourceBuilder().build();
            // 使用 Seata DataSourceProxy 对数据源进行代理
            return new DataSourceProxy(dataSource);
        }
    }
  2. 使用 AT 模式进行事务管理 : 在你的业务代码中,使用 @GlobalTransactional 注解来管理全局事务:

    java 复制代码
    @Service
    public class OrderService {
    
        @Autowired
        private OrderRepository orderRepository;
    
        @GlobalTransactional(timeoutMills = 300000, name = "create-order")
        public void createOrder(Order order) {
            // 创建订单逻辑
            orderRepository.save(order);
    
            // 调用其他服务/数据库操作...
    
            // 如果需要回滚
            if (someCondition) {
                throw new RuntimeException("触发分布式事务回滚");
            }
        }
    }

示例解释

在这个示例中,我们配置了 Seata AT 模式,并在 OrderService 类中使用 @GlobalTransactional 注解声明了一个全局事务。当 createOrder 方法被调用时,Seata 会自动开启一个全局事务。如果在方法执行过程中抛出异常,Seata 会尝试回滚全局事务,确保各个参与的微服务中的本地事务都能回滚到执行前的状态。

请注意,为了确保分布式事务的一致性,调用其他服务进行数据库操作时也需要遵循 Seata 的分布式事务管理规则。这意味着其他服务也需要集成 Seata,并且在涉及数据库操作的方法上使用 @GlobalTransactional 注解。

在使用 AT 模式时,确保 Seata Server 正在运行并且可以被应用访问。此外,由于 AT 模式会对数据库性能产生一定影响,因此在使用前应评估业务场景是否适合。

如果你需要显示地指定使用 AT 模式,可以在 application.yml 中进行以下配置:

yaml 复制代码
seata:
  client:
    rm:
      datasource:
        datasource-type: AT

TCC 模式

TCC(Try-Confirm-Cancel)模式是 Seata 支持的一种分布式事务模式, 适用于业务操作可以明确分为预留资源(Try)、确认执行(Confirm)和取消执行(Cancel)三个步骤的场景。下面是如何在 Spring Boot 集成 Seata 后更改为 TCC 模式,并给出一个使用示例。

更改为 TCC 模式在 Spring Boot 应用中使用 TCC 模式,首先需要确保已经集成了 Seata,并且 Seata Server 正在运行。 然后按照以下步骤进行:

  1. 定义 TCC 接口: 首先定义一个 TCC 接口,该接口需要包含 Try、Confirm 和 Cancel 三个方法。

    java 复制代码
    public interface TccActionOne {
    
        // Try 阶段方法
        @TwoPhaseBusinessAction(name = "prepareCreateOrder", commitMethod = "commit", rollbackMethod = "rollback")
        boolean prepareCreateOrder(BusinessActionContext context, String orderId, String commodityCode, int orderCount);
    
        // Confirm 阶段方法
        boolean commit(BusinessActionContext context);
    
        // Cancel 阶段方法
        boolean rollback(BusinessActionContext context);
    }
  2. 实现 TCC 接口: 接下来实现 TCC 接口,并标记相应的 Confirm 和 Cancel 方法。

    java 复制代码
    @Component
    public class TccActionOneImpl implements TccActionOne {
    
        private Logger logger = LoggerFactory.getLogger(TccActionOneImpl.class);
    
        @Override
        public boolean prepareCreateOrder(BusinessActionContext context, String orderId, String commodityCode, int orderCount) {
            logger.info("prepareCreateOrder");
            // 预留资源逻辑
            return true;
        }
    
        @Override
        public boolean commit(BusinessActionContext context) {
            logger.info("commit");
            // 确认执行逻辑
            return true;
        }
    
        @Override
        public boolean rollback(BusinessActionContext context) {
            logger.info("rollback");
            // 取消执行逻辑
            return true;
        }
    }
  3. 使用 TCC 接口: 在业务代码中通过注入 TCC 接口的实现类来使用 TCC 事务。

    java 复制代码
    @Service
    public class OrderService {
    
        @Autowired
        private TccActionOne tccActionOne;
    
        @GlobalTransactional
        public void createOrder(String orderId, String commodityCode, int orderCount) {
            // Try 阶段
            boolean result = tccActionOne.prepareCreateOrder(new BusinessActionContext(), orderId, commodityCode, orderCount);
            if (!result) {
                throw new RuntimeException("预留资源失败");
            }
            // 其他业务逻辑...
        }
    }
  4. 配置 Seata TCC : 在 application.ymlapplication.properties 中不需要特别为 TCC 模式进行配置,因为 TCC 模式是通过业务代码显式实现的。

yaml 复制代码
 seata:
   client:
     rm:
       datasource:
         datasource-type: TCC

示例解释

在这个示例中,OrderService 类中的 createOrder 方法使用了 @GlobalTransactional 注解来声明一个全局事务。在这个方法中,我们调用了 TccActionOne 接口的 prepareCreateOrder 方法,这是 TCC 事务的 Try 阶段,用于预留必要的业务资源。

如果 Try 阶段成功,Seata 会在全局事务提交时调用 commit 方法;如果 Try 阶段失败,或者业务逻辑抛出异常,Seata 会在全局事务回滚时调用 rollback 方法。

这个示例中的 TccActionOneImpl 类实现了 TCC 接口,并提供了 Try、Confirm 和 Cancel 三个阶段的具体逻辑。在实际应用中,这些方法应该包含实际的业务逻辑,例如在 Try 阶段锁定库存,在 Confirm 阶段扣减库存,在 Cancel 阶段释放锁定的库存。

请注意,这个示例只是为了演示如何在 Spring Boot 中使用 Seata 的 TCC 模式。在实际使用中,你需要根据自己的业务场景实现相应的预留、确认和取消逻辑。此外,确保各个服务之间的接口调用能够正确传递 Seata 的 XID,以保证分布式事务的一致性。

SAGA 模式

Seata 的 SAGA 模式提供了一种长事务的解决方案,它通过定义一系列的状态和事件来管理事务流程。SAGA 模式适用于长事务场景,特别是当事务中的某些步骤需要执行较长时间时, 传统的两阶段提交(2PC)可能不够高效。在 SAGA 模式中,事务被拆分为一系列本地事务,每个本地事务完成后都会发布事件,这些事件会触发下一个本地事务的执行。 如果某个本地事务失败,SAGA 模式会执行补偿事务来回滚之前已经完成的本地事务。

  1. 添加 Seata SAGA 依赖 : 在 Spring Boot 项目的 pom.xml 文件中添加 Seata SAGA Engine 依赖:

    xml 复制代码
    <dependency>
        <groupId>io.seata</groupId>
        <artifactId>seata-saga-engine</artifactId>
        <version>版本号</version>
    </dependency>
  2. 定义状态机: 创建一个状态机配置文件(JSON 或 YAML 格式),定义状态和事件。以下是一个简单的 JSON 格式的状态机配置示例:

    json 复制代码
    {
      "name": "SimpleSaga",
       "version": "1.0",
      "startState": "AddOrder",
      "states": [
        {
          "name": "AddOrder",
          "type": "ServiceTask",
          "serviceUri": "http://order-service/add",
          "next": "SubtractStock",
          "failEnd": true
        },
        {
          "name": "SubtractStock",
          "type": "ServiceTask",
          "serviceUri": "http://stock-service/subtract",
          "compensationTrigger": "StockCompensation",
          "next": "End",
          "failEnd": true
        },
        {
          "name": "StockCompensation",
          "type": "ServiceTask",
          "serviceUri": "http://stock-service/compensate",
          "failEnd": true
        },
        {
          "name": "End",
          "type": "End"
        }
      ],
      "transitions": [],
      "compensationTransitions": []
    }

    在这个示例中,我们定义了一个简单的 SAGA 事务流程,它由两个本地事务组成:AddOrderSubtractStock。如果 SubtractStock 失败,将触发补偿事务 StockCompensation

  3. 配置 SAGA : 在 application.yml 中配置 SAGA 相关参数。例如,指定状态机资源的位置:

    yaml 复制代码
    seata:
     client:
        rm:
          datasource:
            datasource-type: SAGA
      saga:
        state-machine-engine:
          async-thread-pool:
            core-pool-size: 1
            max-pool-size: 20
            queue-capacity: 500
            keep-alive-time: 60
        state-machine-repository:
          type: file
          file:
            dir: classpath*:saga/statelang/  # 状态机定义文件所在目录
  4. 启动状态机: 在业务代码中,使用 Seata SAGA Engine 启动状态机:

    java 复制代码
    @Autowired
    private StateMachineEngine stateMachineEngine;
    
    public void startSaga(String orderId, String commodityCode, int count) {
        Map<String, Object> startParams = new HashMap<>();
        startParams.put("orderId", orderId);
        startParams.put("commodityCode", commodityCode);
        startParams.put("count", count);
    
        stateMachineEngine.start("SimpleSaga", null, startParams);
    }

示例解释

在这个示例中,我们定义了一个名为 SimpleSaga 的状态机,它包含两个 ServiceTask 类型的状态:AddOrderSubtractStock。每个状态都关联了一个服务 URI,这些 URI 应对应于实际的微服务 API。

当 SAGA 开始时,它首先尝试执行 AddOrder 状态关联的服务。如果成功,它将继续执行 SubtractStock 状态关联的服务。如果 SubtractStock 失败,将触发定义在 StockCompensation 状态中的补偿服务。

在 Spring Boot 应用中,我们通过注入 StateMachineEngine 来启动状态机,并传递相关参数。这些参数将作为输入传递给每个 ServiceTask 状态。

请注意,这个示例只是为了演示如何在 Spring Boot 中使用 Seata 的 SAGA 模式。在实际使用中,你需要根据自己的业务场景定义状态机,并实现相应的服务 API。此外,确保服务 API 能够处理 SAGA 传递的参数,并且能够在失败时提供适当的补偿逻辑。

XA 模式

Seata 的 XA 模式是一种基于 XA 协议的分布式事务实现,它使用两阶段提交(2PC)来确保事务的原子性。这种模式适用于需要严格一致性的场景,且数据库或资源管理器支持 XA 协议。 在 Spring Boot 中使用 XA 模式通常涉及到配置支持 XA 的数据源和启用 Seata 的 XA 分布式事务支持。

  1. 添加 Seata 依赖 : 在 Spring Boot 项目的 pom.xml 文件中添加 Seata 依赖:

    xml 复制代码
    <dependency>
        <groupId>io.seata</groupId>
        <artifactId>seata-spring-boot-starter</artifactId>
        <version>版本号</version>
    </dependency>
  2. 配置支持 XA 的数据源: 配置一个支持 XA 事务的数据源。许多流行的连接池,如 HikariCP 或 Atomikos,都提供了对 XA 数据源的支持。

    以下是使用 HikariCP 作为连接池配置 XA 数据源的示例:

    java 复制代码
    @Configuration
    public class DataSourceConfig {
    
        @Bean
        @ConfigurationProperties(prefix = "spring.datasource.hikari")
        public DataSource dataSource() {
            return new HikariDataSource();
        }
    }

    并在 application.yml 中配置 HikariCP 的 XA 数据源属性:

yaml 复制代码
 spring:
   datasource:
     hikari:
       jdbc-url: jdbc:mysql://localhost:3306/your_db
       username: your_username
       password: your_password
       # 其他 HikariCP 配置...
  1. 启用 Seata XA 支持 : 在 application.yml 中启用 Seata 的 XA 支持,并配置 Seata 相关属性:

    yaml 复制代码
    seata:
      enabled: true
      application-id: ${spring.application.name}
      tx-service-group: my_tx_group
      client:
        rm:
          datasource:
            datasource-type: "com.zaxxer.hikari.HikariDataSource"
        tm:
          commit-retry-count: 5
          rollback-retry-count: 5
      service:
        vgroup-mapping:
          my_tx_group: default
      registry:
        type: nacos
        nacos:
          server-addr: 127.0.0.1:8848
      config:
        type: nacos
        nacos:
          server-addr: 127.0.0.1:8848
      client:
        rm:
          datasource:
            datasource-type: XA

    确保 datasource-type 属性设置为你实际使用的 XA 数据源类名。

  2. 使用 XA 模式进行事务管理 : 在你的业务代码中,使用 @GlobalTransactional 注解来管理全局事务:

    java 复制代码
    @Service
    public class OrderService {
    
        @Autowired
        private OrderRepository orderRepository;
    
        @GlobalTransactional
        public void createOrder(Order order) {
            // 创建订单逻辑
            orderRepository.save(order);
    
            // 调用其他服务/数据库操作...
    
            // 如果需要回滚
            if (someCondition) {
                throw new RuntimeException("触发分布式事务回滚");
            }
        }
    }

示例解释

在这个示例中,我们配置了一个支持 XA 的数据源,并在 OrderService 中使用 @GlobalTransactional 注解声明了一个全局事务。当 createOrder 方法被调用时,Seata 会启动一个全局事务。如果在方法执行过程中出现异常,Seata 会尝试回滚全局事务。 请注意,数据库必须支持 XA 协议,否则无法使用 XA 模式。同时,确保 Seata Server 正在运行并可访问。

此外,由于 XA 模式会对性能产生影响,并且实现复杂,因此在选择使用 XA 模式之前,请评估你的业务场景是否真的需要严格的分布式事务一致性。在一些场景下,可能可以通过其他事务模式或者最终一致性的设计来实现业务需求,这些方案往往更加简单和高效。

总结:

更改 Seata 的事务模式通常涉及到对业务代码的修改,以及对 Seata 配置的调整。在切换事务模式时,务必理解每种模式的适用场景和实现细节,并进行充分的测试以确保事务的正确性和一致性。

在本文中,探索了如何在 Spring Boot 应用中集成 Seata,并详细介绍了四种分布式事务模式:AT、TCC、SAGA 和 XA。每种模式都有其独特的适用场景和优势。AT 模式适合快速开发,提供了无缝的分布式事务支持;TCC 模式允许更细粒度的控制,适用于复杂业务逻辑;SAGA 模式适用于长事务处理,优化了系统资源的使用;XA 模式则提供了严格的 XA 协议支持,适用于对数据一致性要求极高的场景。通过合理选择和实施这些模式,开发者可以在分布式环境中有效地管理事务,并确保数据的一致性和系统的稳定性。随着微服务架构的不断演进,Seata 和 Spring Boot 的结合无疑将成为处理复杂分布式事务场景的有力工具。

相关推荐
zmd-zk2 分钟前
kafka+zookeeper的搭建
大数据·分布式·zookeeper·中间件·kafka
时差95323 分钟前
【面试题】Hive 查询:如何查找用户连续三天登录的记录
大数据·数据库·hive·sql·面试·database
计算机-秋大田1 小时前
基于Spring Boot的船舶监造系统的设计与实现,LW+源码+讲解
java·论文阅读·spring boot·后端·vue
CXDNW2 小时前
【网络面试篇】HTTP(2)(笔记)——http、https、http1.1、http2.0
网络·笔记·http·面试·https·http2.0
嚣张农民2 小时前
JavaScript中Promise分别有哪些函数?
前端·javascript·面试
代码之光_19802 小时前
保障性住房管理:SpringBoot技术优势分析
java·spring boot·后端
戴眼镜的猴4 小时前
Spring Boot的过滤器与拦截器的区别
spring boot
尘浮生4 小时前
Java项目实战II基于Spring Boot的光影视频平台(开发文档+数据库+源码)
java·开发语言·数据库·spring boot·后端·maven·intellij-idea
尚学教辅学习资料4 小时前
基于SpringBoot的医药管理系统+LW示例参考
java·spring boot·后端·java毕业设计·医药管理