重新理解 RocketMQ Commit Log 存储协议

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

问题思考

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

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

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

Commit Log 真实分布

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

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

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

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

由上图得知:

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

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

Commit Log 存储协议

关于 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

我整理后, 如下图:

说明 1:我整理后的消息协议编号和代码中不是一致的,代码中只是标明了顺序, 真实物理文件中的存储协议会更详细。说明 2:在我写的《RocketMQ 分布式消息中间件:核心原理与最佳实践》中,这个图缺少了 Body 内容,这里加了,也更详细的补充了其他数据。 这里有几个问题需要说明下:

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

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

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

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

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

用 Golang 解开 RocketMQ Commit Log

RocketMQ 是用 java 写的,根据上文描述的存储协议,我用 Golang 编写了一个工具,可以解开 Commit Log 和 Cosumer Queue,代码地址:https://github.com/rmq-plus-plus/rocketmq-decoder。

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

1、指定 Commit Log 位点,直接解析 Commit Log 中的消息,并且打印。

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

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

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

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

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

回答最初的问题

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

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

是连续的。

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

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

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

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

不是连续的。

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

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

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

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

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

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

讨论说明:由于 RocketMQ 一些版本可能有差异,本文在 4.9.3 版本下讨论,大家可以参考这个方法,解开 5.0 甚至其他版本,其他数据文件的存储协议格式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/60519.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于FPGA的超声波测距——UART串口输出

文章目录 前言一、超声波模块介绍1、产品特点2、超声波模块的时序图 二、系统设计1、系统模块框图2、RTL视图 三、源码1、div_clk_us(1us的分频)2、产生驱动超声波的信号3、串口发送模块4、HC_SR04_uart(顶层文件) 四、效果五、总结六、参考资料 前言 环境&#xff1a; 1、Quar…

阿里云安全组设置

简介​ 云主机安全组必须打开如下端口&#xff1a; ssh&#xff1a;22http&#xff1a;80https&#xff1a;443ftp&#xff1a;21、20000&#xff5e;30000 阿里云安全组端口开放教程​ 腾讯云安全组端口开放教程​ 华为云安全组端口开放教程​

ES6新增的语法

ES6实际上是一个泛指&#xff0c;泛指 ES2015 及后续的版本 1&#xff0c;let用于声明变量的关键字 let 声明的变量只在所处于的代码块内有效 if (true) { let a 10 } console.log(a) // a is not defined 2, let 不存在变量提升 console.log(a) // a is not deined let a 1…

Vue + ElementUI 实现可编辑表格及校验

效果 完整代码见文末 实现思路 使用两个表单分别用于实现修改和新增处理。 通过一个editIndex变量判断是否是编辑状态来决定是否展示输入框&#xff0c;当点击指定行的修改后进行设置即可&#xff1a; <el-table-columnv-for"(column, index) in columns":key&qu…

华为QinQ技术的基本qinq和灵活qinq 2种配置案例

基本qinq配置&#xff1a; 运营商pe设备在收到同一个公司的ce发来的的包&#xff0c;统一打上同样的vlan &#xff0c;如上图&#xff0c;同一个家公司两边统一打上vlan 2&#xff0c;等于在原内网vlan 10或20过来的包再统一打上vlan 2的标签&#xff0c;这样传输就不会和其它…

基于text2vec和faiss开发实现文档查询系统初体验

最近接触到了一些文本向量化的预训练模型&#xff0c;感觉相比较自己去基于gensim去训练词向量来说&#xff0c;使用预训练模型可能是更高效的方式了&#xff0c;正好有一个想法一直在想能够以什么样的形式间接的实现问答&#xff0c;说白了这里的问答跟我们理解的chatGPT类型的…

Spark 3.1.1 遇到的 from_json regexp_replace组合表达式慢问题的解决

背景 目前公司在从spark 2.4.x升级到3.1.1的时候&#xff0c;遇到了一类SQL极慢的情况&#xff0c;该SQL的如下(只列举了关键的)&#xff1a; select device_personas.* from(selectdevice_id, ads_id, from_json(regexp_replace(device_personas, (?<(\\{|,))"devic…

【数据结构】——线性表的相关习题

目录 题型一&#xff08;线性表的存储结构&#xff09;题型二&#xff08;链表的判空&#xff09;题型三&#xff08;单链表的建立&#xff09;题型四&#xff08;顺序表、单链表的插入删除操作&#xff09;题型五&#xff08;双链表的插入删除操作&#xff09;题型六&#xff…

【ARM Coresight 系列文章 2.5 - Coresight 寄存器:PIDR0-PIDR7,CIDR0-CIDR3 介绍】

文章目录 1.1 JEDEC 与 JEP1061.2 PIDR0-PIDR7(peripheral identification registers)1.2 CIDR0-CIDR3(Component Identification Registers) 1.1 JEDEC 与 JEP106 JEDEC和JEP106都是来自美国电子工业联合会&#xff08;JEDEC&#xff0c;Joint Electron Device Engineering C…

SpringBoot单元测试

目录 1.什么是单元测试? 2.单元测试有哪些好处? 3.Spring Boot单元测试使⽤ 单元测试的实现步骤 1. ⽣成单元测试类 2. 添加单元测试代码 2.1 .添加Spring Boot框架测试注解:SpringBootTest 2.2 添加单元测试业务逻辑 简单的断⾔说明 1.什么是单元测试? 单元测试(un…

分页Demo

目录 一、分页对象封装 分页数据对象 分页查询实体类 实体类用到的utils ServiceException StringUtils SqlUtil BaseMapperPlus,> BeanCopyUtils 二、示例 controller service dao 一、分页对象封装 分页数据对象 import cn.hutool.http.HttpStatus; import com.…

Python-flask项目入门

一、flask对于简单搭建一个基于python语言-的web项目非常简单 二、项目目录 示例代码 git路径 三、代码介绍 1、安装pip依赖 通过pip插入数据驱动依赖pip install flask-sqlalchemy 和 pip install pymysql 2.配置数据源 config.py DIALECT mysql DRIVER pymysql USERN…

SQL-每日一题【1164. 指定日期的产品价格】

题目 产品数据表: Products 写一段 SQL来查找在 2019-08-16 时全部产品的价格&#xff0c;假设所有产品在修改前的价格都是 10 。 以 任意顺序 返回结果表。 查询结果格式如下例所示。 示例 1: 解题思路 1.题目要求我们查找在 2019-08-16 时全部产品的价格&#xff0c;假设所…

关于java异常的整理

文章目录 一、异常分类二、throw、throws、try-catch-finally三、CglibAopProxy中对异常的处理4、关于UndeclaredThrowableException 一、异常分类 java异常层级结构 Throwable:所有异常的根接口 Error:严重错误,程序无法处理和恢复 例如VirtualMachineError,OOMError等 Excep…

【图像去噪】基于原始对偶算法优化的TV-L1模型进行图像去噪研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

8.5作业

要求实现AB进程对话 a.A进程先发送一句话给B进程&#xff0c;B进程接收后打印 b.B进程再回复一句话给A进程&#xff0c;A进程接收后打印 c.重复1.2步骤&#xff0c;当收到quit后&#xff0c;要结束AB进程 A进程 #include<stdio.h> #include<string.h> #include&…

【新版系统架构补充】-七层模型

网络功能和分类 计算网络的功能 &#xff1a;数据通信、资源共享、管理集中化、实现分布式处理、负载均衡 网络性能指标&#xff1a;速率、带宽&#xff08;频带宽度或传送线路速率&#xff09;、吞吐量、时延、往返时间、利用率 网络非性能指标&#xff1a;费用、质量、标准化…

【Rust】Rust学习

文档&#xff1a;Rust 程序设计语言 - Rust 程序设计语言 简体中文版 (bootcss.com) 墙裂推荐这个文档 第一章入门 入门指南 - Rust 程序设计语言 简体中文版 第二章猜猜看游戏 猜猜看游戏教程 - Rust 程序设计语言 简体中文版 (bootcss.com) // 导入库 use std::io; use s…

2023.08.01 驱动开发day8

驱动层 #include <linux/init.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_irq.h> #include <linux/interrupt.h> #include <linux/fs.h> #include <linux/gpio.h> #include <linux/of_gpio.h>#…

NVM保姆级安装配置

nvm安装配置 1、NVM简介2、NVM安装三、NVM使用四、NVM常用命令 1、NVM简介 在项目开发过程中&#xff0c;使用到vue框架技术&#xff0c;需要安装node下载项目依赖&#xff0c;但经常会遇到node版本不匹配而导致无法正常下载&#xff0c;重新安装node却又很麻烦。为解决以上问…