根据你提供的三段代码,我来分析为什么在已有OpenFeign服务调用的基础上还要引入RabbitMQ:

主要区别和原因

1. 事务一致性问题

// 原始代码 - 存在分布式事务问题
@Transactional
public void tryPayOrderByBalance(PayOrderFormDTO payOrderFormDTO) {
    // ... 业务逻辑
    tradeClient.markOrderPaySuccess(po.getBizOrderNo()); // Feign调用
}

问题:当支付服务本地事务提交后,如果Feign调用订单服务失败,无法回滚已完成的支付操作。

2. 解决方案对比

方案一:Seata全局事务(注释掉的代码)
@GlobalTransactional // 使用Seata保证分布式事务
public void tryPayOrderByBalance(PayOrderFormDTO payOrderFormDTO) {
    userClient.deductMoney(...);     // 扣减余额
    markPayOrderSuccess(...);        // 更新支付状态
    tradeClient.markOrderPaySuccess(...); // 更新订单状态
}

缺点:性能开销大,架构复杂,需要额外的协调器。

方案二:RabbitMQ消息队列(最终采用的方案)
@Transactional
public void tryPayOrderByBalance(PayOrderFormDTO payOrderFormDTO) {
    // 1. 扣减余额和更新支付状态在本地事务中
    userClient.deductMoney(...);
    markPayOrderSuccess(...);
    
    // 2. 发送MQ消息(事务消息)
    rabbitTemplate.convertAndSend("pay.topic", "pay.success", po.getBizOrderNo());
}

引入RabbitMQ的核心优势

1. 最终一致性保证

  • 支付服务本地事务成功后,通过MQ异步通知订单服务

  • 即使订单服务暂时不可用,消息会在MQ中持久化,确保最终处理

2. 解耦和削峰填谷

  • 解耦:支付服务不需要知道订单服务的具体实现和状态

  • 异步处理:支付成功后立即返回,不需要等待订单状态更新完成

  • 流量削峰:在高并发场景下,MQ可以缓冲请求,避免服务雪崩

3. 可靠性保障

@RabbitListener(bindings = @QueueBinding(...))
public void listenePaySuccessMsg(Long orderId) {
    orderService.markOrderPaySuccess(orderId); // 确保消息被可靠消费
}
  • 消息持久化:即使MQ重启,消息也不会丢失

  • 消费者确认机制:确保消息被成功处理

4. 故障隔离

  • 如果订单服务出现故障,不会影响支付服务的正常运作

  • 支付成功后,消息会在MQ中等待订单服务恢复后处理

总结

方案

一致性

性能

复杂度

可靠性

OpenFeign同步调用

强一致性

Seata全局事务

强一致性

较差

RabbitMQ异步

最终一致性

结论:引入RabbitMQ是为了在保证数据最终一致性的前提下,提高系统性能、可靠性和可扩展性,是分布式系统中常用的解耦方案。

更多推荐