重新理解RocketMQ Commit Log存储协议

简介: Consumer Queue Offset是连续的吗, Commit Log Offset是连续的吗?今天我们一起重新理解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文件可以看到类似下面的数据文件分布:

图片

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

相关实践学习
快速体验阿里云云消息队列RocketMQ版
本实验将带您快速体验使用云消息队列RocketMQ版Serverless系列实例进行获取接入点、创建Topic、创建订阅组、收发消息、查看消息轨迹和仪表盘。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
相关文章
|
消息中间件 存储 数据库
RocketMQ 流存储解析:面向流场景的关键特性与典型案例
RocketMQ 流存储解析:面向流场景的关键特性与典型案例
89123 106
|
消息中间件 物联网 网络性能优化
MQTT常见问题之MQTT不支持5.0的协议如何解决
MQTT(Message Queuing Telemetry Transport)是一个轻量级的、基于发布/订阅模式的消息协议,广泛用于物联网(IoT)中设备间的通信。以下是MQTT使用过程中可能遇到的一些常见问题及其答案的汇总:
|
存储 消息中间件 缓存
RocketMQ 5.0 分级存储背后技术优化包含那几个方面
RocketMQ 5.0 分级存储背后的技术优化是一个综合性的系统工程,需要考虑多个方面,包括存储介质的选择、数据读写策略的设计、数据压缩和解压缩技术的引入、自动的数据分级和迁移机制的实现,以及高可用性和容错性的保证等。这些技术优化的目的是为了实现存储成本和性能的最优平衡,提高系统的可靠性和可用性,满足大规模数据处理的需求。
403 1
|
4月前
|
数据采集 传感器 监控
Modbus 与 MQTT 协议兼容:MyEMS 的泛在能源数据采集技术实现
MyEMS深度融合Modbus与MQTT协议,破解能源数据采集中协议碎片化、网络异构、数据孤岛等难题。通过Modbus接入95%以上工业设备,实现现场数据精准“拉取”;依托MQTT构建高效物联网传输通道,支持多源数据主动“推送”与云端集成。边缘侧采集规整,中心侧汇聚分析,形成统一、可靠、低延迟的数据流。该架构兼具高兼容性、强扩展性与低运维成本,广泛应用于工业园区、商业楼宇及集团型企业,支撑实时监控、AI分析与跨系统融合,打造泛在互联的能源数据底座,助力企业实现全面智慧能源管理。
389 6
|
11月前
|
数据可视化 关系型数据库 MySQL
嵌入式C++、STM32、MySQL、GPS、InfluxDB和MQTT协议数据可视化
通过本文的介绍,我们详细讲解了如何结合嵌入式C++、STM32、MySQL、GPS、InfluxDB和MQTT协议,实现数据的采集、传输、存储和可视化。这种架构在物联网项目中非常常见,可以有效地处理和展示实时数据。希望本文能帮助您更好地理解和应用这些技术,构建高效、可靠的数据处理和可视化系统。
692 82
|
7月前
|
监控 安全 Java
Java 开发中基于 Spring Boot 3.2 框架集成 MQTT 5.0 协议实现消息推送与订阅功能的技术方案解析
本文介绍基于Spring Boot 3.2集成MQTT 5.0的消息推送与订阅技术方案,涵盖核心技术栈选型(Spring Boot、Eclipse Paho、HiveMQ)、项目搭建与配置、消息发布与订阅服务实现,以及在智能家居控制系统中的应用实例。同时,详细探讨了安全增强(TLS/SSL)、性能优化(异步处理与背压控制)、测试监控及生产环境部署方案,为构建高可用、高性能的消息通信系统提供全面指导。附资源下载链接:[https://pan.quark.cn/s/14fcf913bae6](https://pan.quark.cn/s/14fcf913bae6)。
1509 0
|
消息中间件 存储 数据库
深入学习RocketMQ的底层存储设计原理
文章深入探讨了RocketMQ的底层存储设计原理,分析了其如何通过将数据和索引映射到内存、异步刷新磁盘以及消息内容的混合存储来实现高性能的读写操作,从而保证了RocketMQ作为一款低延迟消息队列的读写性能。
|
传感器 网络协议 Ubuntu
MQTT协议与EMQ
MQTT协议与EMQ
478 0
|
数据采集 传感器 监控
多协议网关BL110钡铼6路RS485转MQTT协议云网关
BL110钡铼6路RS485转MQTT协议云网关是一款高性能、易配置的工业级设备,适用于各种需要远程监控和数据采集的物联网应用场景。通过将传统RS485设备的数据转换为MQTT协议并上传至云平台,实现了设备的远程管理和智能控制,极大地提升了系统的管理效率和响应速度。
492 2
|
网络协议 物联网 网络性能优化
物联网协议比较 MQTT CoAP RESTful/HTTP XMPP
【10月更文挑战第18天】本文介绍了物联网领域中四种主要的通信协议:MQTT、CoAP、RESTful/HTTP和XMPP,分别从其特点、应用场景及优缺点进行了详细对比,并提供了简单的示例代码。适合开发者根据具体需求选择合适的协议。
588 5