Hmily TCC模式深度剖析:从理论到实践的完整指南

Hmily TCC模式深度剖析:从理论到实践的完整指南

【免费下载链接】hmily Distributed transaction solutions 【免费下载链接】hmily 项目地址: https://gitcode.com/gh_mirrors/hm/hmily

分布式事务的痛点与TCC解决方案

你是否还在为分布式系统中的数据一致性问题头疼?在微服务架构下,传统单机事务(ACID)已无法满足跨服务调用场景,分布式事务(Distributed Transaction)成为保证数据一致性的关键技术。然而,常见的2PC(两阶段提交)方案存在性能瓶颈,Saga模式又难以处理复杂业务逻辑回滚。Hmily作为一款金融级柔性分布式事务解决方案,其TCC(Try-Confirm-Cancel)模式通过业务侵入式设计,在性能与一致性之间取得了完美平衡。

读完本文你将掌握:

  • TCC模式的核心原理与Hmily实现机制
  • 从零开始的Hmily TCC集成步骤(以Spring Cloud为例)
  • 高并发场景下的TCC优化策略与最佳实践
  • 常见问题诊断与解决方案(含完整代码示例)

TCC模式理论基础

TCC三阶段模型

TCC模式将分布式事务拆分为三个明确的操作阶段,通过业务逻辑的拆分实现分布式场景下的最终一致性:

mermaid

各阶段职责:

  • Try阶段:检查并预留业务资源(如扣减库存前锁定商品数量)
  • Confirm阶段:确认执行业务操作,不做业务检查(如实际扣减库存)
  • Cancel阶段:取消执行业务操作,释放预留资源(如释放锁定的商品数量)

Hmily TCC架构设计

Hmily采用分层架构设计,通过拦截器和事务上下文传递实现TCC模式的自动化管理:

mermaid

核心组件说明:

  • 事务协调器:管理全局事务状态,协调各参与者的Confirm/Cancel操作
  • 事务拦截器:基于AOP拦截@HmilyTCC注解方法,自动传递事务上下文
  • 参与者管理器:维护分布式事务参与者列表,处理幂等性与重试机制

Hmily TCC快速集成指南

环境准备与依赖配置

前置要求:

  • JDK 8+
  • Spring Cloud Greenwich.SR2+
  • Maven 3.5+
  • MySQL 5.7+(事务日志存储)

1. 添加Maven依赖

<!-- Hmily Spring Cloud Starter -->
<dependency>
    <groupId>org.dromara</groupId>
    <artifactId>hmily-spring-boot-starter-springcloud</artifactId>
    <version>2.1.1</version>
</dependency>

<!-- Hmily TCC核心依赖 -->
<dependency>
    <groupId>org.dromara</groupId>
    <artifactId>hmily-tcc</artifactId>
    <version>2.1.1</version>
</dependency>

<!-- 事务日志存储(MySQL) -->
<dependency>
    <groupId>org.dromara</groupId>
    <artifactId>hmily-repository-database-mysql</artifactId>
    <version>2.1.1</version>
</dependency>

2. 配置application.yml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/hmily_tcc?useUnicode=true&characterEncoding=utf8
    username: root
    password: root

hmily:
  service:
    appName: order-service  # 当前微服务名称
  serializer:
    type: kryo  # 序列化方式:kryo/fst/hessian
  repository:
    database:
      driverClassName: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/hmily?useUnicode=true&characterEncoding=utf8
      username: root
      password: root
  tcc:
    delayCancelExe: 60  # 延迟取消执行时间(秒)
    retryMax: 3  # 最大重试次数
    scheduledDelay: 60  # 定时任务延迟时间(秒)
    scheduledThreadMax: 10  # 定时任务线程池大小

代码实现(订单-库存-账户场景)

以电商下单场景为例,实现一个完整的TCC分布式事务:

1. 创建TCC注解接口

public interface OrderService {
    /**
     * 创建订单(TCC事务入口)
     */
    @HmilyTCC(confirmMethod = "confirmCreateOrder", cancelMethod = "cancelCreateOrder")
    OrderDTO createOrder(OrderDTO orderDTO);
    
    /**
     * Confirm阶段:确认创建订单
     */
    void confirmCreateOrder(OrderDTO orderDTO);
    
    /**
     * Cancel阶段:取消创建订单
     */
    void cancelCreateOrder(OrderDTO orderDTO);
}

2. 实现TCC业务逻辑

@Service
public class OrderServiceImpl implements OrderService {
    
    @Autowired
    private OrderMapper orderMapper;
    
    @Autowired
    private InventoryFeignClient inventoryFeignClient;  // 库存服务Feign客户端
    
    @Autowired
    private AccountFeignClient accountFeignClient;  // 账户服务Feign客户端
    
    @Override
    @Transactional
    public OrderDTO createOrder(OrderDTO orderDTO) {
        // 1. 本地保存订单(状态:PENDING)
        OrderDO orderDO = new OrderDO();
        BeanUtils.copyProperties(orderDTO, orderDO);
        orderDO.setStatus(OrderStatus.PENDING);
        orderMapper.insert(orderDO);
        
        // 2. 调用库存服务扣减库存(TCC参与者)
        InventoryDTO inventoryDTO = new InventoryDTO();
        inventoryDTO.setProductId(orderDTO.getProductId());
        inventoryDTO.setQuantity(orderDTO.getQuantity());
        inventoryFeignClient.decrease(inventoryDTO);
        
        // 3. 调用账户服务扣减余额(TCC参与者)
        AccountDTO accountDTO = new AccountDTO();
        accountDTO.setUserId(orderDTO.getUserId());
        accountDTO.setAmount(orderDTO.getTotalAmount());
        accountFeignClient.decrease(accountDTO);
        
        return orderDTO;
    }
    
    @Override
    public void confirmCreateOrder(OrderDTO orderDTO) {
        // 更新订单状态为已确认
        OrderDO orderDO = new OrderDO();
        orderDO.setOrderNo(orderDTO.getOrderNo());
        orderDO.setStatus(OrderStatus.CONFIRMED);
        orderMapper.updateStatus(orderDO);
    }
    
    @Override
    public void cancelCreateOrder(OrderDTO orderDTO) {
        // 更新订单状态为已取消
        OrderDO orderDO = new OrderDO();
        orderDO.setOrderNo(orderDTO.getOrderNo());
        orderDO.setStatus(OrderStatus.CANCELED);
        orderMapper.updateStatus(orderDO);
    }
}

3. 库存服务TCC实现

@Service
public class InventoryServiceImpl implements InventoryService {
    
    @Autowired
    private InventoryMapper inventoryMapper;
    
    @Override
    @HmilyTCC(confirmMethod = "confirmDecrease", cancelMethod = "cancelDecrease")
    public boolean decrease(InventoryDTO inventoryDTO) {
        // Try阶段:锁定库存(预扣减)
        return inventoryMapper.lock(inventoryDTO) > 0;
    }
    
    public boolean confirmDecrease(InventoryDTO inventoryDTO) {
        // Confirm阶段:实际扣减库存
        return inventoryMapper.decrease(inventoryDTO) > 0;
    }
    
    public boolean cancelDecrease(InventoryDTO inventoryDTO) {
        // Cancel阶段:释放锁定的库存
        return inventoryMapper.unlock(inventoryDTO) > 0;
    }
}

4. 数据库表设计

-- 订单表
CREATE TABLE `order_tbl` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `order_no` varchar(64) NOT NULL COMMENT '订单号',
  `user_id` varchar(64) NOT NULL COMMENT '用户ID',
  `product_id` varchar(64) NOT NULL COMMENT '商品ID',
  `quantity` int(11) NOT NULL COMMENT '数量',
  `total_amount` decimal(10,2) NOT NULL COMMENT '总金额',
  `status` int(2) NOT NULL COMMENT '状态:0-待确认,1-已确认,2-已取消',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_order_no` (`order_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';

-- 库存表
CREATE TABLE `inventory_tbl` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `product_id` varchar(64) NOT NULL COMMENT '商品ID',
  `total_inventory` int(11) NOT NULL COMMENT '总库存',
  `locked_inventory` int(11) NOT NULL DEFAULT '0' COMMENT '锁定库存',
  `available_inventory` int(11) NOT NULL COMMENT '可用库存',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_product_id` (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库存表';

Hmily TCC核心原理

事务上下文传递机制

Hmily通过拦截器和ThreadLocal实现事务上下文的跨服务传递:

mermaid

关键代码在TccMethodInterceptor中实现:

public class TccMethodInterceptor implements MethodInterceptor {
    
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        // 获取TCC注解信息
        HmilyTCC hmilyTCC = AnnotationUtils.getAnnotation(invocation.getMethod(), HmilyTCC.class);
        
        // 创建或获取事务上下文
        HmilyTransactionContext context = HmilyTransactionContextLocal.getInstance().get();
        if (context == null) {
            context = new HmilyTransactionContext();
            context.setTransType(hmilyTCC.pattern().name());
            // 创建新的全局事务
            HmilyTransaction transaction = HmilyTransactionManager.getInstance().begin(context);
            context.setTransId(transaction.getTransId());
        }
        
        // 执行目标方法
        Object result = invocation.proceed();
        
        // 提交事务(仅发起者执行)
        if (context.getRole() == HmilyRoleEnum.START.getCode()) {
            HmilyTransactionManager.getInstance().commit();
        }
        
        return result;
    }
}

事务补偿机制

Hmily通过定时任务对悬挂事务进行补偿处理,确保最终一致性:

mermaid

高级特性与性能优化

幂等性设计

TCC模式下必须保证Confirm/Cancel操作的幂等性,推荐实现方式:

  1. 基于唯一ID的幂等
@Service
public class InventoryServiceImpl implements InventoryService {
    
    @Autowired
    private InventoryMapper inventoryMapper;
    
    @Autowired
    private IdGenerator idGenerator;  // 分布式ID生成器
    
    @Override
    public boolean confirmDecrease(InventoryDTO inventoryDTO) {
        // 1. 生成幂等ID(事务ID+业务ID)
        String idempotentId = inventoryDTO.getTransId() + "_" + inventoryDTO.getProductId();
        
        // 2. 检查是否已处理
        if (redisTemplate.hasKey("tcc:confirm:" + idempotentId)) {
            return true;  // 已处理,直接返回成功
        }
        
        // 3. 执行扣减操作
        int rows = inventoryMapper.decrease(inventoryDTO);
        
        // 4. 标记已处理(设置过期时间,如24小时)
        if (rows > 0) {
            redisTemplate.opsForValue().set("tcc:confirm:" + idempotentId, "1", 24, TimeUnit.HOURS);
            return true;
        }
        
        return false;
    }
}
  1. 基于状态机的幂等:在数据库表中增加状态字段,通过乐观锁控制

并发控制策略

高并发场景下TCC模式可能出现资源竞争,推荐采用以下控制策略:

策略实现方式适用场景性能影响
悲观锁SELECT ... FOR UPDATE写冲突频繁
乐观锁WHERE version = ?写冲突较少
分布式锁Redis/ZooKeeper跨服务资源竞争

乐观锁实现示例

-- 乐观锁更新库存
UPDATE inventory_tbl 
SET available_inventory = available_inventory - #{quantity},
    version = version + 1
WHERE product_id = #{productId} 
  AND available_inventory >= #{quantity}
  AND version = #{version}

性能优化实践

  1. 异步Confirm/Cancel:非核心流程采用异步执行
hmily:
  tcc:
    asyncConfirm: true  # 异步Confirm
    asyncCancel: true   # 异步Cancel
    executor:
      threadMax: 20     # 异步线程池大小
  1. 本地消息表优化:将事务日志写入本地数据库,避免远程调用

  2. 批量处理:合并多个小事务为一个大事务,减少网络开销

常见问题与解决方案

1. 事务悬挂问题

问题描述:Cancel操作比Try操作先执行,导致资源无法正确释放。

解决方案:在Cancel方法中增加前置检查:

public void cancelCreateOrder(OrderDTO orderDTO) {
    // 检查Try操作是否已执行
    OrderDO orderDO = orderMapper.selectByOrderNo(orderDTO.getOrderNo());
    if (orderDO == null || orderDO.getStatus() == OrderStatus.INIT) {
        // Try未执行,直接返回成功
        return;
    }
    // 执行Cancel逻辑
    orderMapper.updateStatus(orderDTO.getOrderNo(), OrderStatus.CANCELED);
}

2. 空回滚问题

问题描述:当Try操作超时未执行,触发Cancel操作。

解决方案:增加Try操作的状态记录:

public OrderDTO createOrder(OrderDTO orderDTO) {
    // 1. 插入订单记录(状态:INIT)
    orderMapper.insert(new OrderDO(orderDTO, OrderStatus.INIT));
    
    try {
        // 2. 执行远程调用
        inventoryFeignClient.decrease(inventoryDTO);
        accountFeignClient.decrease(accountDTO);
        
        // 3. 更新状态为TRY_SUCCESS
        orderMapper.updateStatus(orderDTO.getOrderNo(), OrderStatus.TRY_SUCCESS);
    } catch (Exception e) {
        // 4. 更新状态为TRY_FAIL
        orderMapper.updateStatus(orderDTO.getOrderNo(), OrderStatus.TRY_FAIL);
        throw e;
    }
}

3. 幂等性失效

问题描述:重试机制导致重复扣减库存或金额。

解决方案:基于Redis实现分布式锁:

public boolean confirmDecrease(InventoryDTO inventoryDTO) {
    String lockKey = "tcc:lock:" + inventoryDTO.getProductId();
    try (RedisLock lock = new RedisLock(redisTemplate, lockKey, 5000)) {
        if (lock.acquire()) {
            // 执行扣减逻辑
            return inventoryMapper.decrease(inventoryDTO) > 0;
        } else {
            // 获取锁失败,认为已处理
            return true;
        }
    }
}

完整代码示例与部署指南

项目结构

hmily-demo-tcc-springcloud/
├── hmily-demo-tcc-springcloud-order/       # 订单服务
├── hmily-demo-tcc-springcloud-inventory/   # 库存服务
├── hmily-demo-tcc-springcloud-account/     # 账户服务
└── hmily-demo-tcc-springcloud-eureka/      # 注册中心

部署步骤

  1. 克隆代码仓库
git clone https://gitcode.com/gh_mirrors/hm/hmily.git
cd hmily/hmily-demo/hmily-demo-tcc/hmily-demo-tcc-springcloud
  1. 初始化数据库
# 执行SQL脚本
mysql -uroot -p < ../../sql/hmily-demo.sql
  1. 启动服务
# 启动注册中心
cd hmily-demo-tcc-springcloud-eureka
mvn spring-boot:run

# 启动库存服务
cd ../hmily-demo-tcc-springcloud-inventory
mvn spring-boot:run

# 启动账户服务
cd ../hmily-demo-tcc-springcloud-account
mvn spring-boot:run

# 启动订单服务
cd ../hmily-demo-tcc-springcloud-order
mvn spring-boot:run
  1. 测试TCC事务
# 正常场景测试
curl -X POST http://localhost:8083/order/create \
  -H "Content-Type: application/json" \
  -d '{"userId":"1001","productId":"P001","quantity":2,"totalAmount":200.00}'

# 异常场景测试(库存不足)
curl -X POST http://localhost:8083/order/create \
  -H "Content-Type: application/json" \
  -d '{"userId":"1001","productId":"P001","quantity":9999,"totalAmount":999900.00}'

总结与展望

Hmily TCC模式通过精巧的架构设计,解决了分布式事务中的一致性与性能难题。本文从理论到实践详细介绍了TCC模式的实现原理、代码示例和最佳实践,重点关注了:

  • TCC三阶段模型与Hmily事务协调机制
  • 完整的Spring Cloud集成步骤与代码实现
  • 高并发场景下的幂等性设计与性能优化
  • 常见问题(悬挂、空回滚)的解决方案

随着微服务架构的普及,分布式事务将成为后端开发的必备技能。Hmily作为Dromara社区的重要项目,未来将支持更多RPC框架(如gRPC、Dubbo3)和存储引擎,为开发者提供更灵活的事务解决方案。

收藏本文,关注Hmily官方仓库,获取最新技术动态!

下期预告:《Hmily TAC模式详解:零侵入实现分布式事务》

【免费下载链接】hmily Distributed transaction solutions 【免费下载链接】hmily 项目地址: https://gitcode.com/gh_mirrors/hm/hmily

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值