当前位置: 首页 > news >正文

为什么要使用RocketMQ半消息

1,什么是半消息?

半消息,也称为预备消息(Prepared Message),是 RocketMQ 实现分布式事务的一种核心机制。它指的是已经成功发送到 Broker,但是暂时对消费者不可见(即消费者无法消费到)的消息

可以把它理解为一个“预提交”的状态。消息先被存起来,但先不“解锁”,直到后续的确认操作完成。


2,半消息的核心作用

半消息最主要、最核心的作用就是实现分布式事务,确保本地事务执行和消息发送这两个分布式操作之间的最终一致性。

1. 解决“上游事务成功,下游消息必达”的问题

在没有半消息的普通场景下,一个常见的难题是:

  1. 先执行本地数据库事务,然后发送MQ消息。

    • 风险:数据库事务成功,但消息发送失败,导致下游系统无法感知状态变化。

  2. 先发送MQ消息,然后执行本地数据库事务。

    • 风险:消息发送成功,但数据库事务失败或回滚,导致下游系统收到了一个“虚假”的消息。

半消息机制就是为了优雅地解决这个“先有鸡还是先有蛋”的难题。

3,半消息的工作流程(事务消息)

半消息是 RocketMQ 事务消息 实现的一部分。其完整的工作流程如下:


4,典型应用场景

5,总结

特性说明
本质一种对消费者不可见的“预备消息”状态。
核心作用作为 RocketMQ 事务消息的基石,解决分布式系统中的数据最终一致性问题
关键机制二阶段提交(发送半消息 -> 结束事务) + 状态回查(解决超时未决问题)。
最终目的保证:只要下游系统消费到了消息,那上游的本地事务就一定执行成功了

简单来说,半消息就是一个“预占位”操作,先把消息的位置占住,等本地事务出结果后,再决定是“确认生效”还是“取消作废”,从而完美地协调了数据库和消息队列之间的状态,是实现可靠消息传递的关键设计。

6,如果不使用半消息会有什么问题

  1. 发送半消息

    • 生产者向 Broker 发送一条消息。这条消息此时就是一个“半消息”。

    • 关键:Broker 会接收并存储这条消息,但不会将其投递给任何消费者。它对消费者是“隐藏”的。

  2. 执行本地事务

    • 半消息发送成功后,生产者开始执行本地数据库事务(例如:扣减库存、生成订单等)。

  3. 结束事务(提交或回滚)

    • 本地事务执行完成后,生产者会向 Broker 发送一个 结束事务 的请求(commit 或 rollback)。

    • 提交(Commit):如果本地事务执行成功,Broker 会将这条半消息从“隐藏”状态改为“可投递”状态(即成为一个正常消息)。之后,消费者就可以正常消费这条消息了。

    • 回滚(Rollback):如果本地事务执行失败,Broker 会直接删除这条半消息,就像它从来没发送过一样。下游系统自然也就收不到这条消息。

  4. 事务回查(Transaction Check)

    • 这是一个非常重要的容错机制。如果在第3步,生产者因为宕机、网络异常等原因,一直没有向 Broker 发送 commit 或 rollback 指令,这条消息就会一直处于“半消息”的悬而未决状态。

    • RocketMQ 会定期(可配置)向生产者发起 事务状态回查

    • 生产者收到回查后,需要检查本地事务的最终执行结果(例如:查询本地数据库,看订单是否最终创建成功),并根据检查结果再次向 Broker 提交 commit 或 rollback 指令。

  5. 为什么需要半消息?(带来的好处)

  6. 数据最终一致性:确保了业务操作(本地事务)和消息发送要么同时成功,要么同时失败。下游系统消费消息的前提,一定是上游业务逻辑正确执行完毕。

  7. 解耦:将分布式事务的复杂性封装在了 MQ 内部,业务开发者只需关注本地事务的执行和回查逻辑的实现,无需自己实现复杂的分布式事务协议(如两阶段提交)。

  8. 高可用:即使生产者在发送半消息后发生故障,RocketMQ 的事务回查机制也能最终决定消息的命运,避免了消息长时间处于“未知”状态,保证了系统的最终一致性。

  9. 避免脏读:消费者永远不会消费到“对应本地事务失败”的消息,保证了数据的正确性。

  10. 订单系统与积分系统:用户下单成功后,需要给用户增加积分。

    1. 订单服务创建订单(本地事务)。

    2. 同时,发送一条“订单已创建”的半消息。

    3. 如果订单创建成功,则提交半消息,积分系统消费到消息后为用户增加积分。

    4. 如果订单创建失败(例如库存不足),则回滚半消息,积分系统不会收到任何消息。

  11. 支付成功通知:支付成功后,通知其他系统(如发货、发券等)。

  12. 所有需要保证业务操作与事件通知强一致性的场景

方案流程复述:

begin transaction; (开启事务)

update order set status = 'paid' where id = 1; (执行SQL)

mq.send(msg); + retry (发送消息并重试直至成功)

根据第3步的结果:

  • 成功commit transaction; (提交事务)

  • 失败rollback transaction; (回滚事务)

  1. 场景:在步骤3发送消息成功后,步骤4提交之前,应用程序宕机了。

  2. 结果:因为数据库事务还没有提交,所以数据库操作会随着连接断开而自动回滚。同时,消息虽然已经发送到Broker并被消费者可见,但对应的业务数据(订单状态)并没有真的更新。

  3. 数据库事务时间过长(性能问题)

    • 问题网络I/O(发送MQ)是非常耗时的操作。将这个操作放在数据库事务内部,会导致数据库连接占用时间非常长。

    • 后果:数据库连接是宝贵的资源。长时间占用连接会显著降低数据库的吞吐量,成为系统性能的瓶颈。在高并发场景下,可能很快耗尽数据库连接池,导致系统无法响应。

  4. 极端情况下的不一致(虽然概率低,但存在)

    • 场景:步骤3消息发送成功,步骤4执行commit时,数据库节点突然宕机。

    • 结果:这是一个非常极端的情况。数据库可能提交成功也可能回滚,生产者无法确定。这会导致和上面类似的问题:消息发出去了,但不确定数据库事务是否成功。这本质上是分布式事务问题,即如何保证两个系统(数据库和MQ)的操作具有原子性。

  5. 对业务代码的侵入性

    • 业务逻辑必须关心消息发送的重试和事务的最终决策,代码结构被固定化了。

  6. 结论:这会导致下游系统消费到一条“虚假”的消息(例如,积分系统收到了支付成功消息,但订单库中该订单仍是“未支付”)。这虽然是不一致的,但比消息彻底丢失要好,因为消息还在,我们可以通过后续的对账、补偿机制来发现和修复这个不一致。而在消息丢失的方案中,我们连发现问题都很难。

它牺牲了数据库性能,并且仍然存在一个极小概率的不一致窗口

而RocketMQ的半消息方案可以看作是对这个方案的标准化、产品化和优化

  1. 性能优化:它将耗时的网络发送从数据库事务中剥离出去(先发半消息,再执行事务)。

  2. 可靠性强化:通过事务回查机制,作为一个可靠的兜底方案,主动地去消除那个“不一致窗口”,使得整个流程更加健壮和自动化。

因此,要构建一个高性能、高可靠、自动化的分布式系统,RocketMQ的半消息方案是更专业的选择。

http://www.dtcms.com/a/361915.html

相关文章:

  • 使用C#语言 基于FTP协议进行文件夹上传下载
  • 【Android】Span富文本简介
  • 苹果 Safari 地址栏可能被超大光标视觉欺骗
  • 阿里云OSS架构示意图与流程
  • AR眼镜在警务安防的应用方案
  • 前沿科技竞速:脑机接口、AI芯片与半导体生态上的新突破
  • 线性回归中梯度下降与正规方程以及拟合问题与正则化
  • 【职业】算法与数据结构专题
  • 【Flink】DataStream API (二)
  • 收藏!VSCode 开发者工具快捷键大全
  • 计算机毕设推荐:基于python的农产品价格数据分析与预测的可视化系统的设计与实现 基于Python农产品管理系统【源码+文档+调试】
  • 基于单片机汽车防盗系统/汽车安全防丢系统
  • 企业级主流日志系统架构对比ELKK Stack -Grafana Stack
  • 解决「图片导出功能需要 Chromium 浏览器支持,但未找到」的完整方案
  • Promise:异步编程的优雅解决方案
  • elemen ui Table表格中添加图片
  • qData 数据中台【开源版】发布 1.0.4 版本,全面升级数据清洗与资产管理能力
  • Spring Security(第六篇):结营篇 —— 完整源码与后续进阶路线 [特殊字符]
  • Day20 API
  • 什么是最大熵强化学习?
  • Go项目中关于优雅关闭的那些事
  • 动态配置最佳实践:Spring Boot 十种落地方式与回滚审计指南(含实操与避坑)
  • 如何将mysql数据导入人大金仓数据库
  • 漏洞挖掘 渗透测试思路图总结
  • 期货交易策略自动化实现
  • 数组基础及原理
  • 秋招冲刺计划(Day12)
  • Qwen-Image-Edit完全指南:实战20B参数模型的文字与语义-外观双重编辑
  • 如何使用VMware创建一台Ubuntu机器
  • Linux内核内存管理系列博客教程学习规划