RabbitMQ异步和Seata全局事务解决微服务数据最终一致性
·
根据你提供的三段代码,我来分析为什么在已有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是为了在保证数据最终一致性的前提下,提高系统性能、可靠性和可扩展性,是分布式系统中常用的解耦方案。
更多推荐
所有评论(0)