MST
星途 面试题库

面试题:Spring Cloud微服务架构分布式事务一致性保障

假设你正在基于Spring Cloud构建一个电商微服务系统,涉及订单创建、库存扣减和支付等多个微服务,如何确保这些微服务间分布式事务的一致性?请详细说明设计思路和关键代码实现点。
43.7万 热度难度
后端开发微服务架构

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 基于XA协议:使用支持XA协议的数据库,如MySQL 5.7+ 。在订单创建、库存扣减和支付等微服务涉及的数据库操作时,通过XA事务管理器来协调各个数据库资源。但这种方式性能较低,因为事务执行过程中会锁定资源直到整个事务完成。
  2. TCC(Try - Confirm - Cancel)模式
    • Try阶段:各个微服务尝试预留资源,比如订单微服务创建预订单,库存微服务预扣减库存,支付微服务预冻结支付金额。
    • Confirm阶段:当所有Try操作成功后,各个微服务执行真正的提交操作,完成订单创建、库存扣减和支付。
    • Cancel阶段:如果Try阶段有任何一个操作失败,已执行Try操作的微服务进行回滚,释放预留资源。
  3. Saga模式
    • 将一个长事务分解为多个本地事务,每个本地事务对应一个微服务操作。
    • 通过事件驱动的方式来协调各个微服务。例如,订单创建成功后发送一个事件,库存微服务监听该事件并执行库存扣减,库存扣减成功再发送事件通知支付微服务进行支付。
    • 当某个步骤失败时,通过补偿事务来恢复数据一致性,比如库存扣减失败,订单微服务执行取消订单操作。

关键代码实现点

  1. 基于XA协议(以Spring Boot + MySQL为例)
    • 引入依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql - connector - java</artifactId>
</dependency>
- **配置数据源**:
@Bean
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSourceProperties masterDataSourceProperties() {
    return new DataSourceProperties();
}

@Bean
public DataSource masterDataSource() {
    return masterDataSourceProperties().initializeDataSourceBuilder().build();
}

@Bean
public UserTransaction userTransaction() throws Throwable {
    UserTransactionImp userTransactionImp = new UserTransactionImp();
    userTransactionImp.setTransactionTimeout(10000);
    return userTransactionImp;
}

@Bean
public AtomikosDataSourceBean masterXADataSource() throws SQLException {
    AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
    xaDataSource.setXaDataSourceClassName("com.mysql.cj.jdbc.MysqlXADataSource");
    xaDataSource.setUniqueResourceName("masterXA");
    xaDataSource.setXAProperties(masterDataSourceProperties().getProperties());
    return xaDataSource;
}
- **使用事务管理器**:
@Configuration
@EnableTransactionManagement
public class TransactionConfig {

    @Bean
    public PlatformTransactionManager transactionManager() throws Throwable {
        UserTransaction userTransaction = userTransaction();
        TransactionManager transactionManager = new AtomikosTransactionManager();
        transactionManager.setUserTransaction(userTransaction);
        return new JtaTransactionManager(userTransaction, transactionManager);
    }
}
- **在服务层使用事务**:
@Service
public class OrderService {

    @Autowired
    private OrderRepository orderRepository;

    @Transactional
    public void createOrder(Order order) {
        orderRepository.save(order);
        // 调用库存和支付服务,XA事务会自动管理
    }
}
  1. TCC模式(以Hmily框架为例)
    • 引入依赖
<dependency>
    <groupId>org.dromara</groupId>
    <artifactId>hmily - spring - boot - starter</artifactId>
    <version>1.5.2</version>
</dependency>
- **定义Try、Confirm、Cancel方法**:
@Service
public class OrderTccService {

    @HmilyTCC(confirmMethod = "confirmCreateOrder", cancelMethod = "cancelCreateOrder")
    public void tryCreateOrder(Order order) {
        // 创建预订单逻辑
    }

    public void confirmCreateOrder(Order order) {
        // 确认订单创建逻辑
    }

    public void cancelCreateOrder(Order order) {
        // 取消订单创建逻辑
    }
}
  1. Saga模式(以Spring Cloud Stream + Eventuate Tram为例)
    • 引入依赖
<dependency>
    <groupId>io.eventuate.tram</groupId>
    <artifactId>eventuate - tram - spring - cloud - stream - starter</artifactId>
    <version>0.12.0.RELEASE</version>
</dependency>
- **定义事件和事件处理器**:
@Service
public class OrderEventHandler {

    @Autowired
    private OrderRepository orderRepository;

    @StreamListener(TramEventsDefinition.EVENT_IN)
    @TramEventHandler
    public void handleOrderCreatedEvent(OrderCreatedEvent event) {
        Order order = orderRepository.findById(event.getOrderId()).orElse(null);
        if (order!= null) {
            // 处理订单创建事件,如发送库存扣减事件
        }
    }
}
- **发送事件**:
@Service
public class OrderService {

    @Autowired
    private TramEventPublisher tramEventPublisher;

    public void createOrder(Order order) {
        orderRepository.save(order);
        OrderCreatedEvent event = new OrderCreatedEvent(order.getId());
        tramEventPublisher.publish(TramEventsDefinition.EVENT_OUT, event);
    }
}