首页 > 其他分享 >重新理解RocketMQ Commit Log存储协议

重新理解RocketMQ Commit Log存储协议

时间:2023-04-11 17:57:48浏览次数:44  
标签:存储 Log Offset Commit RocketMQ 字节

本文作者:李伟,社区里大家叫小伟,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文件可以看到类似下面的数据文件分布:

图片

Broker真实数据文件存储分布

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

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

图片

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

图片

append加锁

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

3Commit Log存储协议

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

图片

ChatGPT回复

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

图片

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的任何代码,纯粹是依靠协议解码。

图片

golang-import

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

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

图片

Golang-demo

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

图片

读取consumer-queue-commit-log

5回答最初的问题

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

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

是连续的。

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

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

图片

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版本下讨论。

标签:存储,Log,Offset,Commit,RocketMQ,字节
From: https://www.cnblogs.com/RocketMQ/p/17307094.html

相关文章

  • 一文读懂面试官都在问的Log4J2漏洞
    CVE-2021-44228漏洞简介ApacheLog4j2是一个基于Java的日志记录工具,当前被广泛应用于业务系统开发,开发者可以利用该工具将程序的输入输出信息进行日志记录。2021年11月24日,阿里云安全团队向Apache官方报告了ApacheLog4j2远程代码执行漏洞。该漏洞是由于ApacheLog4j2某些功......
  • LogStash简要介绍
    1简介logstash是一个灵活的数据传输和处理系统,开发语言是JRuby,当然是为了与Java平台对接,不过与Ruby语法兼容良好,非常简洁强大,经常与ElasticSearch,Kibana配置,组成著名的ELK技术栈,非常适合用来做日志数据的分析。Logstash的任务,就是将各种各样的数据,经过配置转化规则,统一......
  • Android开发中Dialog填充满父容器
    Android开发中Dialog填充满父容器在Android原生开发中,通常会使用自定义的Dailog来设计二级面板,其自带一个黑色透明的遮蔽效果。但是想要将Dialog填充满父容器,是需要一些尝试的。环境介绍自定义Dialog类,加载自定义布局layout并进行数据绑定,同时创建接口进行信息传递。其中布局......
  • [golang]使用logrus自定义日志模块
    简介logrus是一个第三方日志库,性能虽不如zap和zerolog,但方便易用灵活。logrus完全兼容标准的log库,还支持文本、JSON两种日志输出格式。特点相较于标准库,logrus有更细致的日志级别,从高到低分别是:trace>debug>info>warn>error>fatal>panic支持自定义日志格式,内置支......
  • git reset [--soft| --mixed| --hard] [commit]
    【参考】https://www.jianshu.com/p/c6927e80a01d【理解】--soft改变最轻,将已提交变成uncommit状态,工作区内容不变--mixed次之,将已提交变成unstage状态,工作区不变--hard最严重,全部撤回,工作区改变 执行gitreset--hard 后可使用gitreflog查看更改 ......
  • m基于shepp-Logan模型和滤波反投影的医学图像多尺度全局重建和局部重建matlab仿真
    1.算法描述        从投影重建物体的截面图像是图像处理中非常重要的技术此技术在物体的无损伤性检测其内部缺陷的应用中能起很大作用从投影重建图像的技术早在20世纪中期就已经制成常规医疗诊断设备的商品1917年奥地利数学家J.Radon发表的论文证明了二维物体或三维物体......
  • R语言MCMC的lme4二元对数Logistic逻辑回归混合效应模型分析吸烟、喝酒和赌博影响数据|
    原文下载链接:http://tecdat.cn/?p=29196最近我们被客户要求撰写关于逻辑回归混合效应模型的研究报告,包括一些图形和统计输出。吸烟、喝酒和赌博被认为是由许多因素造成的。Logistic回归分析是一个非常有效的模型,可以检验各种解释变量和二元反应变量之间的关系。同时,双变量模型分......
  • ubuntu 20.04服务器 syslog客户端日志发送至深信服日志系统服务器中
    1.ubuntu设置Linux下syslog客户端配置:修改配置⽂件:vi/etc/rsyslog.conf在最后⼀⾏加⼊:*.*@192.168.190.50:514#将所有消息发送到该地址重启服务:#servicersyslogrestart查看服务状态#servicersyslog2.日志服务器设置添加源设置源名称----接入类型(syslog)---源IP(你......
  • Loki采集Mysql errorlog,你值得拥有的错误日志聚合系统
    说到分布式日志存储系统,大家肯定对ELK、EFK这些工具并不陌生。可是它们都基于Elasticsearch存储,搭建复杂,耗资源,上手难。所以,个人非常推崇Grafana Labs开源的Loki 轻量级日志聚合分析系统Loki使用标签来作为索引,而不是对全文进行检索,所以在搜索上会有一定的局限性。但是,也能满......
  • window.showModalDialog与window.open
    //window.showModalDialog全屏显示functionwinModalFullScreen(strURL){varsheight=screen.height-70;varswidth=screen.width-10;varwinoption="dialogHeight:"+sheight+"px;dialogWidth:"+swidth+"px;status:yes;scroll:yes;resiz......