paascloud-master中分布式事务补偿:本地消息表实现

paascloud-master中分布式事务补偿:本地消息表实现

【免费下载链接】paascloud-master spring cloud + vue + oAuth2.0全家桶实战,前后端分离模拟商城,完整的购物流程、后端运营平台,可以实现快速搭建企业级微服务项目。支持微信登录等三方登录。 【免费下载链接】paascloud-master 项目地址: https://gitcode.com/gh_mirrors/pa/paascloud-master

你是否在微服务架构中遇到过分布式事务一致性问题?当订单系统、库存系统、支付系统分属不同服务时,如何确保跨服务操作的数据一致性?本文将介绍paascloud-master项目中基于本地消息表的分布式事务补偿方案,带你从零理解实现原理并掌握实操方法。

分布式事务痛点与解决方案对比

在分布式系统中,传统的ACID事务无法跨服务边界保证一致性。paascloud-master作为企业级微服务项目,面临着典型的分布式事务场景:

  • 订单创建后需要扣减库存
  • 支付完成后需要更新订单状态并通知物流
  • 退款流程涉及多服务数据回滚

项目中采用了本地消息表方案,与其他方案对比优势如下:

方案实现复杂度性能一致性适用场景
2PC/3PC强一致性短事务、核心业务
TCC最终一致性业务逻辑简单场景
SAGA最终一致性长事务、复杂回滚
本地消息表最终一致性非实时性要求场景

paascloud-master选择本地消息表方案的核心原因是实现简单且对业务侵入性低,特别适合电商订单等场景。

本地消息表实现原理

本地消息表方案基于"本地事务+消息队列"的思想,核心流程如下:

mermaid

在paascloud-master中,这一流程通过以下组件协同实现:

  • 本地消息表:记录待发送的跨服务事务消息
  • 消息发送器:确保消息可靠投递到队列
  • 消息消费者:处理跨服务事务并返回结果
  • 定时补偿任务:重试失败的消息

项目中的核心实现

本地消息表设计

本地消息表定义在paascloud-provider-omc模块中,核心字段包括消息ID、业务类型、状态等:

CREATE TABLE `tpc_message` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `message_key` varchar(64) NOT NULL COMMENT '消息唯一标识',
  `message_body` text NOT NULL COMMENT '消息内容',
  `message_status` int(11) NOT NULL COMMENT '消息状态:0-待发送,1-已发送,2-已消费,3-失败',
  `business_type` int(11) NOT NULL COMMENT '业务类型',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime NOT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_message_key` (`message_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='本地消息表';

对应的实体类定义在paascloud-provider-tpc-api/src/main/java/com/paascloud/provider/model/dto/TpcMessageDto.java中,包含消息的完整属性定义。

消息发送实现

消息发送器在paascloud-provider-tpc/src/main/java/com/paascloud/provider/service/impl/TpcMqMessageServiceImpl.java中实现,核心逻辑如下:

@Transactional(rollbackFor = Exception.class)
public void saveAndSendMessage(TpcMessageDto messageDto) {
    // 1. 保存消息到本地消息表
    TpcMessage message = new TpcMessage();
    BeanUtils.copyProperties(messageDto, message);
    message.setMessageStatus(MessageStatusEnum.TO_BE_SENT.getMessageStatus());
    messageMapper.insertSelective(message);
    
    // 2. 发送消息到MQ
    try {
        mqProducer.send(messageDto.getTopic(), messageDto.getTag(), 
                        messageDto.getMessageKey(), messageDto.getMessageBody());
        // 3. 更新消息状态为已发送
        message.setMessageStatus(MessageStatusEnum.SENT.getMessageStatus());
        messageMapper.updateByPrimaryKeySelective(message);
    } catch (Exception e) {
        log.error("消息发送失败,等待补偿机制重试:{}", e.getMessage());
        // 发送失败不回滚事务,由补偿任务处理
    }
}

补偿任务实现

定时补偿任务在paascloud-provider-tpc/src/main/java/com/paascloud/provider/service/impl/TpcMqMessageServiceImpl.java中,通过@Scheduled注解实现定时执行:

@Scheduled(cron = "0 0/1 * * * ?") // 每分钟执行一次
public void handleWaitingConfirmMessage() {
    // 查询状态为"待发送"且超过重试间隔的消息
    List<TpcMessage> messageList = messageMapper.selectWaitingConfirmMessage();
    
    for (TpcMessage message : messageList) {
        // 检查重试次数
        if (message.getRetryCount() >= maxRetryCount) {
            message.setMessageStatus(MessageStatusEnum.FAIL.getMessageStatus());
            messageMapper.updateByPrimaryKeySelective(message);
            continue;
        }
        
        // 重试发送
        try {
            mqProducer.send(message.getTopic(), message.getTag(), 
                           message.getMessageKey(), message.getMessageBody());
            message.setMessageStatus(MessageStatusEnum.SENT.getMessageStatus());
            message.setRetryCount(message.getRetryCount() + 1);
            messageMapper.updateByPrimaryKeySelective(message);
        } catch (Exception e) {
            log.error("补偿发送消息失败:{}", e.getMessage());
        }
    }
}

实际应用场景

以订单创建后扣减库存为例,完整流程实现如下:

  1. 订单服务创建订单并发送扣减库存消息

    // 订单服务中
    @Transactional
    public void createOrder(OrderDto orderDto) {
        // 创建订单
        orderMapper.insert(orderDto);
    
        // 发送扣减库存消息
        TpcMessageDto messageDto = new TpcMessageDto();
        messageDto.setTopic("stock_topic");
        messageDto.setTag("deduct");
        messageDto.setMessageKey(UUID.randomUUID().toString());
        messageDto.setMessageBody(JSON.toJSONString(orderDto));
    
        tpcMqMessageService.saveAndSendMessage(messageDto);
    }
    
  2. 库存服务消费消息并处理

    // 库存服务中
    @RabbitListener(queues = "stock_queue")
    public void handleDeductStock(String message) {
        try {
            OrderDto orderDto = JSON.parseObject(message, OrderDto.class);
            // 扣减库存
            stockService.deductStock(orderDto.getProductId(), orderDto.getQuantity());
    
            // 手动确认消息
            channel.basicAck(deliveryTag, false);
        } catch (Exception e) {
            // 消息处理失败,拒绝签收,消息将重回队列等待重试
            channel.basicNack(deliveryTag, false, true);
        }
    }
    
  3. 补偿机制保障最终一致性 当库存服务暂时不可用时,消息会进入重试队列,由补偿任务定时重试,直到库存扣减成功或达到最大重试次数。

核心代码模块路径

总结与最佳实践

paascloud-master中的本地消息表方案通过"本地事务+消息队列+定时补偿"三重机制,实现了分布式事务的最终一致性,具有以下优势:

  • 实现简单,基于成熟的关系型数据库和消息队列
  • 对业务侵入性低,原有业务逻辑改动小
  • 可靠性高,通过重试机制确保消息最终送达

在实际应用中,建议:

  1. 根据业务重要性设置合理的重试次数和间隔
  2. 对失败消息建立人工介入机制
  3. 结合监控告警及时发现长期失败的消息
  4. 核心业务可考虑与TCC方案结合使用

通过本文介绍的方案,你可以在paascloud-master项目中快速实现可靠的分布式事务处理,确保微服务架构下的数据一致性。更多实现细节可参考项目源代码及官方文档。

【免费下载链接】paascloud-master spring cloud + vue + oAuth2.0全家桶实战,前后端分离模拟商城,完整的购物流程、后端运营平台,可以实现快速搭建企业级微服务项目。支持微信登录等三方登录。 【免费下载链接】paascloud-master 项目地址: https://gitcode.com/gh_mirrors/pa/paascloud-master

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

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

抵扣说明:

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

余额充值