RocketMQ 消息存储流程

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

基于 RocketMQ 4.2.0 版本进行的源码分析。
本文讲述 RocketMQ 存储一条消息的流程。

一、存储位置

当有一条消息过来之后,Broker 首先需要做的是确定这条消息应该存储在哪个文件里面。在 RocketMQ 中,这个用来存储消息的文件被称之为 MappedFile。这个文件默认创建的大小为 1GB。
image.png
一个文件为 1GB 大小,也即 1024 * 1024 * 1024 = 1073741824 字节,这每个文件的命名是按照总的字节偏移量来命名的。例如第一个文件偏移量为 0,那么它的名字为 00000000000000000000;当当前这 1G 文件被存储满了之后,就会创建下一个文件,下一个文件的偏移量则为 1GB,那么它的名字为 00000000001073741824,以此类推。
image.png
默认情况下这些消息文件位于 $HOME/store/commitlog 目录下,如下图所示:
image.png

二、文件创建

当 Broker 启动的时候,其会将位于存储目录下的所有消息文件加载到一个列表中:
image.png
当有新的消息到来的时候,其会默认选择列表中的最后一个文件来进行消息的保存:
image.png

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;
    }
    
}

其上述整体流程如下所示:
image.png
等待 MappedFile 创建完毕之后,其便会从请求表 requestTable 中取出并删除表中记录:

public class AllocateMappedFileService extends ServiceThread {

    public MappedFile putRequestAndReturnMappedFile(String nextFilePath,
                                                    String nextNextFilePath,
                                                    int fileSize) {
        // ...
        AllocateRequest r
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值