首发于工号【BiggerBoy】,原文链接

——“半消息藏在这里,但为什么你偷看也没用?”


上篇《RocketMQ系列笔记(三):消息模型与高阶玩法,顺序事务消息拿捏指南》中提到“TBW102是RocketMQ预留的事务消息Topic”,是什么意思?为什么消费者看不到半消息?是如何实现的?带着这些疑问,开启今天的探索之旅!


这句话隐藏的意思就是:生产者发送事务消息时实际Broker会把半消息存储到TBW102这个Topic。当生产者通知Broker提交时,Broker会将消息发送到生产者指定的Topic,此时消费者才能消费该消息。

以下是更详细的流程说明:

事务消息:一手交钱一手交货

再来回顾一下事务消息的流程:

发送半消息

  • 生产者发送事务消息时,首先将消息作为半消息发送到Broker
  • 这个半消息会被存储在RocketMQ的默认事务Topic TBW102
  • 此时,消息对消费者是不可见的,因为它还没有被正式提交

执行本地事务

  • 半消息提交成功后,生产者执行本地事务逻辑(例如数据库操作)
  • 本地事务的执行结果(成功或失败)决定了后续的操作

通知Broker提交或回滚

  • 提交如果本地事务成功,生产者会通知Broker提交半消息。此时,Broker会将消息从 TBW102 移动到生产者指定的目标Topic,消息对消费者可见
  • 回滚如果本地事务失败,生产者会通知Broker回滚半消息。此时,Broker会丢弃 TBW102 中的半消息,消息不会被发送到目标Topic

事务消息的奥秘

消息可见性

  • 半消息阶段消息存储在 TBW102,对消费者不可见
  • 提交后消息移动到目标Topic,对消费者可见

为什么设计为不可见?

  • 事务消息的核心设计
    • RocketMQ的事务消息机制是为了保证消息的最终一致性
    • 在半消息阶段,消息对消费者不可见,是为了避免消费者处理到未确认的消息(可能导致数据不一致)
    • 只有在生产者明确提交事务后,消息才会被移动到目标Topic,对消费者可见

简单来说

TBW102里的消息就像“加密文件”,只有事务完成后才会被“解密”到正确的位置。

配置

  • 自定义半消息Topic可通过配置transactionTopic参数指定其他Topic
  • Broker配置在Broker配置文件中设置transactionTopic参数

注意事项

  • 可以自定义通过Broker配置transactionTopic参数,灵活指定事务消息的存储位置
  • 但没必要乱动如果没有特殊需求,直接用默认的TBW102反而更省心
  • 核心原则事务Topic是RocketMQ内部机制,业务代码无需感知,你的重点仍是本地事务和最终消息的一致性
  • 避免冲突不要在生产环境中使用TBW102作为业务Topic,以免冲突
  • 监控与维护需监控和维护事务Topic,确保事务消息机制正常运行

总结

  • 半消息存储在 TBW102,对消费者不可见
  • 提交后消息移动到目标Topic,对消费者可见
  • 回滚消息被丢弃,不会发送到目标Topic

这种机制确保了事务消息的原子性,即消息的发送与本地事务的执行结果保持一致。


有的小伙伴就会好奇问了:如果消费者订阅TWB102呢,不就可以消费了吗?刚刚说“生产者通知Broker回滚消息,Broker会丢弃TWB102中的半消息”,这里的丢弃是从Broker中删除吗?不会违背RocketMQ顺序写的特性吗?

这是个好问题!下面来揭秘一下:


消费者订阅 TBW102 的情况

  • 理论上可以订阅消费者确实可以订阅 TBW102 这个Topic
  • 实际意义有限
    • TBW102是RocketMQ内部用于存储半消息的Topic,这些消息还未被确认提交或回滚
    • 即使消费者订阅了 TBW102,也只能看到未完成事务的半消息,这些消息的状态是不确定的(可能最终会被提交或回滚)
    • 从业务逻辑上来说,消费这些半消息是没有意义的,因为它们可能最终会被丢弃

划重点
哪怕你订阅了TBW102,看到的也只是“未确认状态”的半消息,它们可能下一秒就被删除或转移,毫无业务价值。


消息被丢弃的含义

  • 回滚时的处理
    • 当生产者通知Broker回滚事务时,Broker会将 TBW102 中对应的半消息标记为已回滚
    • 这些消息会被标记为已删除,消费者无法再访问这些消息

实际上被回滚的半消息还存储在Commitlog中,只是被标记为已删除,之所以没有物理删除和RocketMQ的存储机制有一定关系。

RocketMQ的存储机制

  • CommitLog
    • RocketMQ将所有消息(包括事务消息)顺序写入一个统一的文件,称为 CommitLog
    • 这种设计保证了高性能的写入,因为磁盘只需要顺序追加数据
  • ConsumeQueue
    • 每个Topic的消息索引存储在 ConsumeQueue 中,消费者通过 ConsumeQueue 快速定位消息
  • 事务消息的特殊性
    • 事务消息(包括半消息)也会写入 CommitLog,但在事务未完成时,不会将消息索引写入目标Topic的 ConsumeQueue

消息删除的实现原理

删除操作的本质:RocketMQ的“删除”并不是直接从 CommitLog 中物理擦除数据,而是通过以下方式实现:标记删除:对于回滚的事务消息,RocketMQ会将其标记为“已删除”跳过消费:消费者从ConsumeQueue读取消息时,会跳过被标记为删除的消息物理删除:RocketMQ会定期执行文件清理(默认72小时),将过期的文件(包括已删除的消息)从磁盘中删除顺序写的保持:

    • 删除操作不会影响 CommitLog 的顺序写特性,因为删除只是标记消息状态,而不是立即修改 CommitLog 文件
    • 物理删除是通过清理整个文件(而不是单独删除某条消息)来实现的,因此不会破坏顺序写的性能

结论

删除操作既不会破坏顺序写,也不会让磁盘“千疮百孔”,RocketMQ早就想好了怎么优雅“扔垃圾”。

总结

  • 半消息 存储在 TBW102,对消费者不可见。
  • 提交后 消息移动到目标Topic,对消费者可见。
  • 回滚 消息被丢弃,不会发送到目标Topic。

因此,RocketMQ的事务消息机制通过 TBW102 和半消息的设计,确保了消息的可靠性和一致性,避免了消费者处理到未确认的消息。

好了,今天的分享就到这里啦!如果对你有帮助,辛苦转发➕关注,感谢支持~

image.png


biggerboy
81 声望0 粉丝

引用和评论

0 条评论