基于Spring Cloud Alibaba用Seata和TCC模式搞定分布式事务
在微服务架构盛行的当下,分布式事务管理成为开发过程中绕不开的关键难题。今天,我们就借助Seata和TCC模式,并基于Spring Cloud Alibaba框架,通过一个电商案例来深入探讨如何实现分布式事务管理。
一、Seata与TCC模式基础介绍
1.1 Seata简介
Seata(Simple Extensible Autonomous Transaction Architecture)是一款开源的分布式事务解决方案,主要为微服务架构提供高性能且易用的分布式事务支持。它支持多种事务模式,其中TCC(Try-Confirm-Cancel)模式凭借其灵活性和高性能,在互联网场景尤其是电商、金融等领域得到广泛应用。
Seata包含几个核心组件:
- TC(Transaction Coordinator):即事务协调者,负责对全局事务的状态进行管理,掌控整个事务的走向。
- TM(Transaction Manager):也就是事务管理器,用于定义全局事务的范围,并负责发起事务的提交或回滚操作。
- RM(Resource Manager):资源管理器,主要管理分支事务,承担与数据库交互的工作。
1.2 TCC模式解析
TCC模式是一种基于补偿机制的分布式事务模式,整个过程分为三个阶段:
- Try阶段:尝试执行业务逻辑,这个阶段主要是预留相关资源。以电商场景为例,就是冻结库存,确保后续业务有资源可用。
- Confirm阶段:确认阶段,用于提交业务逻辑。对应到电商场景,就是真正扣减库存,完成业务操作。
- Cancel阶段:取消阶段,主要进行回滚操作。比如释放之前冻结的库存,保证数据的一致性。
TCC模式适用于对高并发和高性能有较高要求的场景,通过在业务代码中显式定义补偿逻辑,给予开发者更大的操作灵活性。
二、电商系统案例分析
2.1 业务场景与系统架构
假设我们正在构建一个电商系统,该系统包含以下几个关键微服务:
- 订单服务(Order Service):负责创建订单,并记录用户相关信息。
- 库存服务(Inventory Service):主要承担商品库存的管理工作。
- 支付服务(Payment Service):用于处理用户的支付流程。
在用户下单时,系统需要完成一系列操作:创建订单记录、冻结商品库存、扣款并记录支付状态。只要其中任何一个步骤出现问题,就必须回滚所有操作,以此保证数据的一致性。
2.2 数据库表设计
为了支撑系统功能,设计了以下数据库表:
- 订单服务表:
CREATE TABLE `orders` ( `id` BIGINT AUTO_INCREMENT PRIMARY KEY, `user_id` BIGINT NOT NULL, `product_id` BIGINT NOT NULL, `quantity` INT NOT NULL, `total_amount` DECIMAL(10, 2) NOT NULL, `status` VARCHAR(20) NOT NULL DEFAULT 'PENDING' -- PENDING, CONFIRMED, CANCELLED );
该表用于存储订单相关信息,包括订单编号、用户ID、商品ID、购买数量、总金额以及订单状态等。
- 库存服务表:
CREATE TABLE `inventory` ( `id` BIGINT AUTO_INCREMENT PRIMARY KEY, `product_id` BIGINT NOT NULL, `total_stock` INT NOT NULL, `frozen_stock` INT NOT NULL DEFAULT 0 );
此表记录商品库存情况,包含商品ID、总库存数量以及冻结库存数量。
- 支付服务表:
CREATE TABLE `payment` ( `id` BIGINT AUTO_INCREMENT PRIMARY KEY, `order_id` BIGINT NOT NULL, `user_id` BIGINT NOT NULL, `amount` DECIMAL(10, 2) NOT NULL, `status` VARCHAR(20) NOT NULL DEFAULT 'PENDING' -- PENDING, SUCCESS, FAILED );
支付服务表主要存储支付相关信息,如订单ID、用户ID、支付金额以及支付状态。
2.3 服务设计与调用实现
我们借助Spring Cloud Alibaba、Seata TCC和Feign来实现服务间的调用。
- 订单服务(Order Service):订单服务在整个事务中扮演全局事务发起者的角色,负责协调库存和支付服务。
- TCC接口定义:
public interface OrderTccService { @TwoPhaseBusinessAction(name = "OrderTccAction", commitMethod = "confirm", rollbackMethod = "cancel") void tryCreate(OrderDTO orderDTO, @BusinessActionContextParameter(paramName = "orderId") Long orderId); boolean confirm(BusinessActionContext context); boolean cancel(BusinessActionContext context); }
该接口定义了订单服务在TCC模式下的三个核心方法,tryCreate
用于尝试创建订单,confirm
用于确认订单操作,cancel
用于取消订单操作。
- **接口实现**:
@Service public class OrderTccServiceImpl implements OrderTccService { @Autowired private OrderMapper orderMapper; @Autowired private InventoryClient inventoryClient; @Autowired private PaymentClient paymentClient; @Override @Transactional public void tryCreate(OrderDTO orderDTO, Long orderId) { Order order = new Order(); order.setUserId(orderDTO.getUserId()); order.setProductId(orderDTO.getProductId()); order.setQuantity(orderDTO.getQuantity()); order.setTotalAmount(orderDTO.getTotalAmount()); order.setStatus("PENDING"); orderMapper.insert(order); // 调用库存服务冻结库存 inventoryClient.freezeStock(order.getProductId(), order.getQuantity()); // 调用支付服务冻结金额 paymentClient.freezePayment(order.getId(), order.getTotalAmount()); } @Override public boolean confirm(BusinessActionContext context) { Long orderId = (Long) context.getActionContext("orderId"); Order order = orderMapper.selectById(orderId); order.setStatus("CONFIRMED"); orderMapper.updateById(order); return true; } @Override public boolean cancel(BusinessActionContext context) { Long orderId = (Long) context.getActionContext("orderId"); Order order = orderMapper.selectById(orderId); order.setStatus("CANCELLED"); orderMapper.updateById(order); return true; } }
在实现类中,tryCreate
方法在创建订单记录后,调用库存服务和支付服务进行资源预留。confirm
和cancel
方法分别用于处理订单确认和取消时的逻辑。
- **Feign客户端**:
@FeignClient(name = "inventory-service") public interface InventoryClient { @PostMapping("/inventory/freeze") void freezeStock(@RequestParam("productId") Long productId, @RequestParam("quantity") Integer quantity); } @FeignClient(name = "payment-service") public interface PaymentClient { @PostMapping("/payment/freeze") void freezePayment(@RequestParam("orderId") Long orderId, @RequestParam("amount") BigDecimal amount); }
Feign客户端用于实现订单服务与库存服务、支付服务之间的远程调用。
- 库存服务(Inventory Service):库存服务主要实现冻结、确认和取消库存的逻辑。
- TCC接口:
public interface InventoryTccService { @TwoPhaseBusinessAction(name = "InventoryTccAction", commitMethod = "confirm", rollbackMethod = "cancel") void freezeStock(@BusinessActionContextParameter(paramName = "productId") Long productId, Integer quantity); boolean confirm(BusinessActionContext context); boolean cancel(BusinessActionContext context); }
定义了库存服务在TCC模式下的相关方法。
- **接口实现**:
@RestController @RequestMapping("/inventory") public class InventoryTccServiceImpl implements InventoryTccService { @Autowired private InventoryMapper inventoryMapper; @Override @PostMapping("/freeze") @Transactional public void freezeStock(Long productId, Integer quantity) { Inventory inventory = inventoryMapper.selectByProductId(productId); if (inventory.getTotalStock() - inventory.getFrozenStock() < quantity) { throw new RuntimeException("Insufficient stock"); } inventory.setFrozenStock(inventory.getFrozenStock() + quantity); inventoryMapper.updateById(inventory); } @Override public boolean confirm(BusinessActionContext context) { Long productId = (Long) context.getActionContext("productId"); Inventory inventory = inventoryMapper.selectByProductId(productId); Integer quantity = (Integer) context.getActionContext("quantity"); inventory.setFrozenStock(inventory.getFrozenStock() - quantity); inventory.setTotalStock(inventory.getTotalStock() - quantity); inventoryMapper.updateById(inventory); return true; } @Override public boolean cancel(BusinessActionContext context) { Long productId = (Long) context.getActionContext("productId"); Inventory inventory = inventoryMapper.selectByProductId(productId); Integer quantity = (Integer) context.getActionContext("quantity"); inventory.setFrozenStock(inventory.getFrozenStock() - quantity); inventoryMapper.updateById(inventory); return true; } }
freezeStock
方法用于检查库存并冻结相应数量的库存,confirm
和cancel
方法分别处理确认和取消操作时的库存变化。
- 支付服务(Payment Service):支付服务负责处理支付冻结和扣款操作。
- TCC接口:
public interface PaymentTccService { @TwoPhaseBusinessAction(name = "PaymentTccAction", commitMethod = "confirm", rollbackMethod = "cancel") void freezePayment(@BusinessActionContextParameter(paramName = "orderId") Long orderId, BigDecimal amount); boolean confirm(BusinessActionContext context); boolean cancel(BusinessActionContext context); }
定义了支付服务在TCC模式下的操作方法。
- **接口实现**:
@RestController @RequestMapping("/payment") public class PaymentTccServiceImpl implements PaymentTccService { @Autowired private PaymentMapper paymentMapper; @Override @PostMapping("/freeze") @Transactional public void freezePayment(Long orderId, BigDecimal amount) { Payment payment = new Payment(); payment.setOrderId(orderId); payment.setAmount(amount); payment.setStatus("PENDING"); paymentMapper.insert(payment); } @Override public boolean confirm(BusinessActionContext context) { Long orderId = (Long) context.getActionContext("orderId"); Payment payment = paymentMapper.selectByOrderId(orderId); payment.setStatus("SUCCESS"); paymentMapper.updateById(payment); return true; } @Override public boolean cancel(BusinessActionContext context) { Long orderId = (Long) context.getActionContext("orderId"); Payment payment = paymentMapper.selectByOrderId(orderId); payment.setStatus("FAILED"); paymentMapper.updateById(payment); return true; } }
freezePayment
方法用于冻结支付金额,confirm
和cancel
方法分别处理支付成功和取消时的逻辑。
2.4 全局事务发起
在订单服务的控制器中,使用@GlobalTransactional
注解来发起全局事务:
@RestController @RequestMapping("/order") public class OrderController { @Autowired private OrderTccService orderTccService; @PostMapping("/create") @GlobalTransactional public String createOrder(@RequestBody OrderDTO orderDTO) { orderTccService.tryCreate(orderDTO, null); return "Order created successfully"; } }
当用户发起创建订单请求时,@GlobalTransactional
注解会开启一个全局事务,确保整个下单流程的数据一致性。
三、总结
通过Seata的TCC模式,我们成功在电商系统中实现了分布式事务管理。在这个过程中,订单服务作为TM发起全局事务,库存服务和支付服务作为RM分别处理各自的资源预留和补偿逻辑。借助Spring Cloud Alibaba的Feign完成服务间的调用,利用MyBatis实现数据库操作。这种方案在高并发场景下,既保证了数据的一致性,又具备良好的性能表现,为电商系统等分布式应用的开发提供了可靠的事务管理方式。