概述:
在现代微服务架构中,处理跨服务的数据一致性是一个复杂且挑战性的问题。随着服务拆分的细化,传统的事务管理机制不再适用,这就需要一种新的解决方案来确保分布式系统中事务的一致性。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 模式
-
添加 Seata 依赖 : 在 Spring Boot 项目的
pom.xml
文件中添加 Seata Starter 依赖:xml<dependency> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> <version>版本号</version> </dependency>
-
配置 Seata : 在
application.yml
中配置 Seata 相关属性,包括服务分组、服务名、注册中心和配置中心的配置:yamlseata: 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
-
配置数据源代理: 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); } }
-
使用 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 正在运行。 然后按照以下步骤进行:
-
定义 TCC 接口: 首先定义一个 TCC 接口,该接口需要包含 Try、Confirm 和 Cancel 三个方法。
javapublic 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); }
-
实现 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; } }
-
使用 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("预留资源失败"); } // 其他业务逻辑... } }
-
配置 Seata TCC : 在
application.yml
或application.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 模式会执行补偿事务来回滚之前已经完成的本地事务。
-
添加 Seata SAGA 依赖 : 在 Spring Boot 项目的
pom.xml
文件中添加 Seata SAGA Engine 依赖:xml<dependency> <groupId>io.seata</groupId> <artifactId>seata-saga-engine</artifactId> <version>版本号</version> </dependency>
-
定义状态机: 创建一个状态机配置文件(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 事务流程,它由两个本地事务组成:
AddOrder
和SubtractStock
。如果SubtractStock
失败,将触发补偿事务StockCompensation
。 -
配置 SAGA : 在
application.yml
中配置 SAGA 相关参数。例如,指定状态机资源的位置:yamlseata: 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/ # 状态机定义文件所在目录
-
启动状态机: 在业务代码中,使用 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 类型的状态:AddOrder
和 SubtractStock
。每个状态都关联了一个服务 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 分布式事务支持。
-
添加 Seata 依赖 : 在 Spring Boot 项目的
pom.xml
文件中添加 Seata 依赖:xml<dependency> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> <version>版本号</version> </dependency>
-
配置支持 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 配置...
-
启用 Seata XA 支持 : 在
application.yml
中启用 Seata 的 XA 支持,并配置 Seata 相关属性:yamlseata: 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 数据源类名。 -
使用 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 的结合无疑将成为处理复杂分布式事务场景的有力工具。