Rocketmq刷盘三剑客

目录

一、MappedFile

二、Rocketmq三种刷盘方式

2.1、CommitRealTimeService

2.2、GroupCommitService

2.3、FlushRealTimeService

三、小结


一、MappedFile

MappedFile是文件映射到内存的结构。根据transientStorePoolEnable配置的不同其操作的内存位置也不同。如果transientStorePoolEnable=true,则其直接操作堆外内存,该对外内存会被刷新到pageCache当中,并最终落盘。如果transientStorePoolEnable=false,则MappedFile直接操作pageCache当中,并由操作系统刷盘线程最终落盘。

MappedFile有两处优化:预热和mlock锁。预热是为了让其常驻内存,mlock锁是将其锁定在内存,避免其换出到swap或者磁盘上去。因为换出到这两个地方都会导致操作文件速度下降很多倍。这是一个高性能中间件不能接受的结果。

二、Rocketmq三种刷盘方式

public DefaultFlushManager() {
            if (FlushDiskType.SYNC_FLUSH == CommitLog.this.defaultMessageStore.getMessageStoreConfig().getFlushDiskType()) {
                this.flushCommitLogService = new CommitLog.GroupCommitService();
            } else {
                this.flushCommitLogService = new CommitLog.FlushRealTimeService();
            }

            this.commitLogService = new CommitLog.CommitRealTimeService();
        }

DefaultFlushManager是CommitLog的一个属性,也就是说针对每一个commitlog文件,都有一个刷盘线程

DefaultFlushManager随着CommitLog初始化而初始化,CommitLog启动而启动

2.1、DefaultFlushManager初始化

public DefaultFlushManager() {
            if (FlushDiskType.SYNC_FLUSH == CommitLog.this.defaultMessageStore.getMessageStoreConfig().getFlushDiskType()) {
                this.flushCommitLogService = new CommitLog.GroupCommitService();
            } else {
                this.flushCommitLogService = new CommitLog.FlushRealTimeService();
            }

            this.commitLogService = new CommitLog.CommitRealTimeService();
        }

DefaultFlushManager初始化的时候如果刷盘策略为同步则初始化GroupCommitService,如果是异步在初始化FlushRealTimeService。不管同步还是异步都会初始化CommitRealTimeService。

2.2、DefaultFlushManager启动

 public void start() {
            this.flushCommitLogService.start();

            if (defaultMessageStore.getMessageStoreConfig().isTransientStorePoolEnable()) {
                this.commitLogService.start();
            }
        }

如果transientStorePoolEnable为true broker角色不为slave,则会启动commitLogService。当然flushCommitLogService必须提前启动。

组合

策略

FlushRealTimeService + CommitRealTimeService

刷盘策略叫异步+缓存刷盘策略

FlushRealTimeService

异步刷盘策略

GroupCommitService

同步刷盘策略

接下来介绍三个线程任务执行流程。

2.1、CommitRealTimeService

CommitRealTimeService是一个线程任务,启动后会执行以下步骤

1、计算当前时间是否大于(上一次提交时间+broker设置commitDataThoroughInterval)。如果大于则

commitDataLeastPages=0.否则commitDataLeastPages=4,做到批量刷盘。

2、调用mappedFileQueue.commit 提交数据,

2.1、找到带提交的commitLog文件

2.2、如果找到了会到调用mappedFile.commit做提交动作,该动作就是将writeBuffer写入fileChannel的过程。

3、如果有数据提交到fileChannle则调用CommitLog.this.flushManager.wakeUpFlush();唤醒刷盘线程执行刷盘。

4、暂停commitIntervalCommitLog时间,默认是200毫秒,再做一次循环。

该任务就是针对transientStorePoolEnable=true的情况,将对外内存数据写入pageCache,并唤醒刷新任务进行刷盘。

2.2、GroupCommitService

GroupCommitService针对的是同步刷盘策略,每10毫秒会执行该任务。

GroupCommitService任务抓取requestsRead请求,并获取当前已刷盘位置和请求需要刷盘位置大小关系

如果已刷盘大小小于请求刷盘位置,则说明pagecache有数据页没有刷新到磁盘,调用以下代码进行刷盘

CommitLog.this.mappedFileQueue.flush(0);

最终调用

int offset = mappedFile.flush(flushLeastPages);

返回刷盘位移,用该offset位移和commitLog文件位移相加,就是当前刷新位置的绝对位移。

如果最终刷盘位移大于当前请求刷盘位移,会跳出刷盘操作。

GroupCommitService虽然叫同步刷盘策略,其实做了同步转异步的动作,所以最终要根据req的CompletableFuture类型字段返回结果。

req.wakeupCustomer(flushOK ? PutMessageStatus.PUT_OK : PutMessageStatus.FLUSH_DISK_TIMEOUT);

再自上而下看下同步刷盘流程

1、broker获取远程client的写请求

SendMessageProcessor#sendMessage

2、sendMessage方法调用MessageStore#putMessage将数据写入。该方法直接将写入commitLog过程同步转异步

public PutMessageResult putMessage(MessageExtBrokerInner msg) {
        return waitForPutResult(asyncPutMessage(msg));
    }

asyncPutMessage(msg)最终调用异步请求获取写入commitLog结果。

this.commitLog.asyncPutMessage(msg)

该方法的最后一行就是刷盘结果

return handleDiskFlushAndHA(putMessageResult, msg, needAckNums, needHandleHA);

所以如果刷盘成功GroupCommitService通过调用wakeupCustomer方法给CompleteFuture一个PUT_OK结果,最终SendMessageProcessor#handlePutMessageResult方法会处理PUT_OK,给客户端一个成功的结果。

同步刷盘同步转异步是通过将刷盘请求包装成GroupCommitRequest并将其写入GroupCommitService的requestsWrite队列。

GroupCommitService线程任务通过每次将requestsWrite队列里面的请求转换到requestsRead并执行刷盘任务实现。之所以需要两个队列,也是减少读写锁竞争,提高并发性。

2.3、FlushRealTimeService

FlushRealTimeService负责异步刷盘

该线程任务启动之后执行以下步骤:

1、获取配置flushCommitLogTimed ,该flushCommitLogTimed=true,直接使用线程睡眠方式等待interval(默认500毫秒)时间;如果flushCommitLogTimed=false,CountDownLatch2对象的await方法睡眠interval时间。两种方式主要是新老版本兼容。

2、设置printFlushProgress,如果该值为true才会输出日志,避免每次都输出日志导致日志过大。这种优化在rocketmq里面随处可见。

3、获取配置的flushCommitLogLeastPages,默认是4,该值是为了批量刷盘使用的页个数,也就是16K。如果不够刷盘页数本次刷盘是不执行的。不能每次请求都要刷盘,过于频繁的刷盘效率太低,另外,如果当前时间>(最后一次刷盘时间+flushPhysicQueueThoroughInterval),会设置flushCommitLogLeastPages=0,导致立即刷盘,不计算当前要刷盘页面是否大于某个值。flushCommitLogThoroughInterval默认为10秒钟,也就是说如果10秒钟过去还未刷屏,会强刷一次盘。

4、mappedFileQueue.flush执行刷盘。

①获取待刷新的mappedFile,这个mappedFile跟根据transientStorePoolEnable配置的不同有两种。每次刷新完成都会把最新刷新位移同步到flushedWhere当中。

②、调用mappedFile的flush方法

2.1、获取待写数据位置

2.2、如果使用了对外内存调用this.fileChannel.force(false);。因为在提交的时候writeBuffer数据已经放入到了fileChannel当中,所以直接刷fileChannel即可

2.3、如果不是对外内存,直接刷新pageCache,即this.mappedByteBuffer.force()

2.4、更新刷新位置flushedPosition

自顶向下考虑下异步刷盘时机。

FlushRealTimeService在CommitLog启动的时候启动,但是启动之后并没有做立即刷盘动作,而是在第一次梳理刷盘请求的时候执行了commitLogService.wakeup();,使用CountDownLatch2对象启动了任务循环体。

三、小结

rocketmq有三个线程负责刷盘,根据不同的配置场景rocketmq的刷盘策略分为三种:1、异步刷盘+缓存刷盘策略 ;2、异步刷盘策略,3同步刷盘策略。本文通过介绍三个负责刷盘线程的执行步骤,交代三种策略的具体内容。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值