基于 RocketMQ 4.2.0 版本进行的源码分析。
本文讲述 RocketMQ 存储一条消息的流程。
一、存储位置
当有一条消息过来之后,Broker 首先需要做的是确定这条消息应该存储在哪个文件里面。在 RocketMQ 中,这个用来存储消息的文件被称之为 MappedFile。这个文件默认创建的大小为 1GB。

一个文件为 1GB 大小,也即 1024 * 1024 * 1024 = 1073741824 字节,这每个文件的命名是按照总的字节偏移量来命名的。例如第一个文件偏移量为 0,那么它的名字为 00000000000000000000;当当前这 1G 文件被存储满了之后,就会创建下一个文件,下一个文件的偏移量则为 1GB,那么它的名字为 00000000001073741824,以此类推。

默认情况下这些消息文件位于 $HOME/store/commitlog 目录下,如下图所示:

二、文件创建
当 Broker 启动的时候,其会将位于存储目录下的所有消息文件加载到一个列表中:

当有新的消息到来的时候,其会默认选择列表中的最后一个文件来进行消息的保存:

public class MappedFileQueue {
public MappedFile getLastMappedFile() {
MappedFile mappedFileLast = null;
while (!this.mappedFiles.isEmpty()) {
try {
mappedFileLast = this.mappedFiles.get(this.mappedFiles.size() - 1);
break;
} catch (IndexOutOfBoundsException e) {
//continue;
} catch (Exception e) {
log.error("getLastMappedFile has exception.", e);
break;
}
}
return mappedFileLast;
}
}
当然如果这个 Broker 之前从未接受过消息的话,那么这个列表肯定是空的。这样一旦有新的消息需要存储的时候,其就得需要立即创建一个 MappedFile 文件来存储消息。
RocketMQ 提供了一个专门用来实例化 MappedFile 文件的服务类 AllocateMappedFileService。在内存中,也同时维护了一张请求表 requestTable 和一个优先级请求队列 requestQueue 。当需要创建文件的时候,Broker 会创建一个 AllocateRequest 对象,其包含了文件的路径、大小等信息。然后先将其放入 requestTable 表中,再将其放入优先级请求队列 requestQueue 中:
public class AllocateMappedFileService extends ServiceThread {
public MappedFile putRequestAndReturnMappedFile(String nextFilePath,
String nextNextFilePath,
int fileSize) {
// ...
AllocateRequest nextReq = new AllocateRequest(nextFilePath, fileSize);
boolean nextPutOK = this.requestTable.putIfAbsent(nextFilePath, nextReq) == null;
if (nextPutOK) {
// ...
boolean offerOK = this.requestQueue.offer(nextReq);
}
}
}
服务类会一直等待优先级队列是否有新的请求到来,如果有,便会从队列中取出请求,然后创建对应的 MappedFile,并将请求表 requestTable 中 AllocateRequest 对象的字段 mappedFile 设置上值。最后将 AllocateRequest 对象上的 CountDownLatch 的计数器减 1 ,以标明此分配申请的 MappedFile 已经创建完毕了:
public class AllocateMappedFileService extends ServiceThread {
public void run() {
// 一直运行
while (!this.isStopped() && this.mmapOperation()) {
}
}
private boolean mmapOperation() {
req = this.requestQueue.take();
if (req.getMappedFile() == null) {
MappedFile mappedFile;
// ...
mappedFile = new MappedFile(req.getFilePath(), req.getFileSize());
// 设置上值
req.setMappedFile(mappedFile);
}
// ...
// 计数器减 1
req.getCountDownLatch().countDown();
// ...
return true;
}
}
其上述整体流程如下所示:

等待 MappedFile 创建完毕之后,其便会从请求表 requestTable 中取出并删除表中记录:
public class AllocateMappedFileService extends ServiceThread {
public MappedFile putRequestAndReturnMappedFile(String nextFilePath,
String nextNextFilePath,
int fileSize) {
// ...
AllocateRequest r

本文详细解析了RocketMQ4.2.0版本中消息的存储过程,包括消息文件的创建、初始化、加载,以及写入策略和刷盘机制,介绍了同步和异步刷盘的不同策略。
最低0.47元/天 解锁文章
1275

被折叠的 条评论
为什么被折叠?



