重新理解RocketMQ Commit Log存储协议

这篇具有很好参考价值的文章主要介绍了重新理解RocketMQ Commit Log存储协议。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本文作者:李伟,社区里大家叫小伟,Apache RocketMQ Committer,RocketMQ Python客户端项目Owner ,Apache Doris Contributor,腾讯云RocketMQ开发工程师。

最近突然感觉:很多软件、硬件在设计上是有root reason的,不是by desgin如此,而是解决了那时、那个场景的那个需求。一旦了解后,就会感觉在和设计者对话,了解他们的思路,学习他们的方法,思维同屏:活到老学到老。

1大家思考

1.1 Consumer Queue Offset是连续的吗, 为什么?

1.2 Commit Log Offset是连续的吗, 为什么?

1.3 Java写的文件,默认是大端序还是小端序,为什么?

2Commit Log真实分布

在大家思考之际, 我们回想下commit log是怎么分布的呢?

在Broker配置的存储根目录下,通过查看Broker实际生成的commit log文件可以看到类似下面的数据文件分布:

重新理解RocketMQ Commit Log存储协议

Broker真实数据文件存储分布

可以看到,真实的存储文件有多个, 每一个都是以一串类似数字的字符串作为文件名的,并且大小1G。

我们结合源码可以知道,实际的抽象模型如下:

重新理解RocketMQ Commit Log存储协议

Commit Log存储文件分布抽象

由上图得知:

  • Commit Log是一类文件的称呼,实际上Commit Log文件有很多个, 每一个都可以称为Commit Log文件。

如图中表示了总共有T个Commit Log文件,他们按照由过去到现在的创建时间排列。

  • 每个Commit Log文件都保存消息, 并且是按照消息的写入顺序保存的,并且总是在写创建时间最大的文件,并且同一个时刻只能有一个线程在写。

如图中第1个文件,1,2,3,4...表示这个文件的第几个消息,可以看到第1234个消息是第1个Commit Log文件的最后一个消息,第1235个消息是第2个Commit Log的第1个消息。

说明1:每个Commit Log文件里的全部消息实际占用的存储空间大小<=1G。这个问题大家自行思考下原因。

说明2:每次写Commit Log时, RocketMQ都会加锁,代码片段见https://github.com/apache/rocketmq/blob/7676cd9366a3297925deabcf27bb590e34648645/store/src/main/java/org/apache/rocketmq/store/CommitLog.java#L676-L722

重新理解RocketMQ Commit Log存储协议

append加锁

我们看到Commit Log文件中有很多个消息,按照既定的协议存储的,那具体协议是什么呢, 你是怎么知道的呢?

3Commit Log存储协议

关于Commit Log存储协议,我们问了下ChatGPT, 它是这么回复我的,虽然不对,但是这个回复格式和说明已经非常接近答案了。

重新理解RocketMQ Commit Log存储协议

ChatGPT回复

我们翻看源码,具体说明下:https://github.com/apache/rocketmq/blob/rocketmq-all-4.9.3/store/src/main/java/org/apache/rocketmq/store/CommitLog.java#L1547-L1587

重新理解RocketMQ Commit Log存储协议

Commit Log存储协议

我整理后, 如下图;

重新理解RocketMQ Commit Log存储协议

我理解的Commit Log存储协议

说明1:我整理后的消息协议编号和代码中不是一致的,代码中只是标明了顺序, 真实物理文件中的存储协议会更详细。

说明2:在我写的《RocketMQ分布式消息中间件:核心原理与最佳实践》中,这个图缺少了Body内容,这里加了,也更详细的补充了其他数据。

这里有几个问题需要说明下:

  • 二进制协议存在字节序,也就是常说的大端、小端。大小端这里不详细说明感兴趣的同学自己google或者问ChatGPT,回答肯定比我说的好。

  • 在java中, 一个byte占用1个字节,1个int占用4个字节,1个short占用2个字节,1个long占用8个字节。

  • Host的编码并不是简单的把IP:Port作为字符串直接转化为byte数组,而是每个数字当作byte依次编码。在下一节的Golang代码中会说明。

  • 扩展信息的编码中,使用了不可见字符作为分割,所以扩展字段key-value中不能包含那2个不可见字符。具体是哪2个,大家找找?

我们看到这个协议后,如何证明你的物理文件就是按照这个协议写的呢?

4用Golang解开RocketMQ Commit Log

RocketMQ是用java写的,根据上文描述的存储协议,我用Golang编写了一个工具,可以解开Commit Log和Cosumer Queue,代码地址:

https://github.com/rmq-plus-plus/rocketmq-decoder

这个工具目前支持2个功能:

  • 指定Commit Log位点,直接解析Commit Log中的消息,并且打印。
  • 指定消费位点,先解析Consumer Queue,得到Commit Log Offset后,再根据Commit Log Offset直接解析Commit Log,并且打印。

在Golang中没有依赖RocketMQ的任何代码,纯粹是依靠协议解码。

重新理解RocketMQ Commit Log存储协议

golang-import

这里贴了一段golang中解析Commit Log Offset的例子:在java中这个offset是一个long类型,占用8个字节。

在golang中,读取8个字节长度的数据,并且按照大端序解码为int64,就可以得到正常的Commit Log Offset。

重新理解RocketMQ Commit Log存储协议

Golang-demo

我跑了一个demo结果,大家参考:

重新理解RocketMQ Commit Log存储协议

读取consumer-queue-commit-log

5回答最初的问题

以下为个人见解,大家参考:

1.1 Consumer Queue Offset是连续的吗, 为什么?

是连续的。

consumer queue offset,是指每个queue中索引消息的下标,下标当然是连续的。消费者也是利用了这个连续性,避免消费位点提交空洞的。

每个索引消息占用相同空间,都是20字节,结构如下:

重新理解RocketMQ Commit Log存储协议

consumer-queue索引消息结构

这里物理位点也就是Commit Log Offset。

1.2 Commit Log Offset是连续的吗, 为什么?

不是连续的。

Commit Log Offset是指的每个消息在全部Commit Log文件中的字节偏移量, 每个消息的大小是不确定的,所以Commit Log Offset,也即是字节偏移量肯定是不一样的。

并且可以知道,每两个偏移量的差的绝对值就是前一个消息的消息字节数总长度。

并且上文中图 “Commit Log存储文件分布抽象”中的有误解,每个小方格的大小其实是不一样的。

1.3 Java写的文件,默认是大端序还是小端序,为什么?

大端序。字节序其实有数据存储顺序和网络传输顺序两种,java中默认用的大端序,保持和网络传输一样,这样方便编解码。

每段网络传输层的数据报文最前面的字节是表达后面的数据是用什么协议传输的,这样数据接收者在接受数据时, 按照字节顺序,先解析协议,再根据协议解码后面的字节序列,符合人类思考和解决问题的方式。

以上是我的理解,有任何问题,可以进社区群细聊。

讨论说明:由于RocketMQ一些版本可能有差异,本文在4.9.3版本下讨论。文章来源地址https://www.toymoban.com/news/detail-410675.html

到了这里,关于重新理解RocketMQ Commit Log存储协议的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • innodb_flush_log_at_trx_commit 和 sync_binlog 参数解析

    这两个参数和MySQL的一致性以及性能相关,默认配置大多数情况下不是最优的。一般来说,互联网线上系统的配置: innodb_flush_log_at_trx_commit —— 0 sync_binlog —— 1000 如果我们想要提交一个事务了,会根据一定的策略把 redo 日志从 redo log buffer 刷入到磁盘文件里去。通过 innod

    2024年02月03日
    浏览(31)
  • 有趣且重要的Git知识合集(8)git commit 重新提交(—amend —no-edit)

    当我们已经 commit提交过一次 了,然后发现还有些代码没改完,这个时候,很多童鞋都会考虑,再commit一次就行了,但是在git记录中就会出现两条commit,其实问题不大,但是如果有很多这种情况,就会使git变得混乱不堪,那么此时最好的情况,就是将 多条commit合并在一起 示例

    2024年02月16日
    浏览(44)
  • git commit遇到with ‘#‘ will be ignored, and an empty message aborts the commit.或git log失败的原因及两种解决方案。

    git add与commit操作已经搞完了,git log碰到下面的问题 或者 git提交时,使用了git commit 文件名 时弹出了以下窗口  文段翻译过来的大意为: 请为本次修改键入commit命令的相关消息,以#开头的行被挡住注释(被忽略),一个空的消息(啥都不输入)将视为放弃本次commit提交。 1、改用

    2024年02月16日
    浏览(50)
  • 二、RocketMQ消息存储源码分析

    Broker模块涉及到的内容非常多,本课程重点讲解以下技术点: 1、Broker启动流程分析 2、消息存储设计 3、消息写入流程 4、亮点分析:NRS与NRC的功能号设计 5、亮点分析:同步双写数倍性能提升的CompletableFuture 6、亮点分析:Commitlog写入时使用可重入锁还是自旋锁? 7、亮点分析

    2024年02月16日
    浏览(38)
  • kafka的log存储解析

    kafka 的 log 存储解析 ——topic 的分区 partition 分段 segment 以及索引等 引言 Kafka 中的 Message 是以 topic 为基本单位组织的,不同的 topic 之间是相互独立的。每个 topic 又可以分成几个不同的 partition( 每个 topic 有几个 partition 是在创建 topic 时指定 的 ) ,每个 partition 存储一部分

    2024年02月08日
    浏览(25)
  • RocketMQ13-事务消息的理解

    RocketMQ采用了2PC(二阶段提交)的思想来实现了提交事务消息,同时增加一个补偿逻辑来处理二阶段超时或者失败的消息,如下图所示。     根据上图可以有一个大概的理解,其中分为两个流程:正常事务消息的发送及提交、事务消息的补偿流程。 2.1事务消息发送及提交 : 生

    2024年01月17日
    浏览(36)
  • 全网最细RocketMQ源码四:消息存储

    看完上一章之后,有没有很好奇,生产者发送完消息之后,server是如何存储,这一章节就来学习 SendMessageProcessor.processRequest 实际真正的负责存储就是DefaultMessageStore, 不过在讲述DefaultMessageStore的时候,我们是自底往上学,因为DefaultMessageStore比较复杂,从顶往下学容易学乱。先

    2024年01月16日
    浏览(40)
  • 13.RocketMQ之消息的存储与发送

    分布式队列因为有高可靠性的要求,所以数据要进行持久化存储。 消息生成者发送消息 Broker收到消息,将消息进行持久化,在存储中新增一条记录 返回ACK给生产者 Broker消息给对应的消费者,然后等待消费者返回ACK 如果消息消费者在指定时间内成功返回ack,那么MQ认为消息消

    2024年02月11日
    浏览(44)
  • Kafka Log存储解析以及索引机制

      在Kafka架构,不管是生产者Producer还是消费者Consumer面向的都是Topic。Topic是逻辑上的概念,而Partition是物理上的概念。每个Partition逻辑上对应一个log文件,该log文件存储是Producer生产的数据。Producer生产的数据被不断追加到该log文件末端,且每条数据都有自己的offset。Kafk

    2024年02月07日
    浏览(29)
  • rocketmq使用mqtt协议

    rocketmq从4.9.3开始,可以兼容mqtt协议,需要安装编译一个rocketmq-mqtt工程,参考:https://rocketmq.apache.org/zh/docs/4.x/mqtt/02RocketMQMQTTQuickStart/ 需要安装rocketmq4.9.3以上的版本 安装过程略 broker.conf配置文件中添加参数,开启多队列分发特性 安装maven配置环境变量 过程略 下载并打包 下面

    2024年02月17日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包