章
目
录
在微服务架构盛行的当下,分布式事务管理成为开发过程中绕不开的关键难题。今天,我们就借助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实现数据库操作。这种方案在高并发场景下,既保证了数据的一致性,又具备良好的性能表现,为电商系统等分布式应用的开发提供了可靠的事务管理方式。