RocketMQ文件刷盘机制与过期文件删除-中间件专区论坛-技术-SpringForAll社区

RocketMQ文件刷盘机制与过期文件删除

文件刷盘机制

RocketMQ 的消息是存储在磁盘上的,这样做有两个优点:

  • 保证断电后恢复

  • 让存储的消息量超出内存的限制

RocketMQ 存储与读写是基于 JDK NIO 的内存映射机制,具体使用 MappedByteBuffer(基于MappedByteBuffer 操作大文件的方式,其读写性能极高)RocketMQ 的消息是存储到磁盘上的,这样既能保证断电后恢复,又可以让存储的消息 超出内存的限制 RocketMQ 为了提高性能,会尽可能地保证磁盘的顺序写 消息在通过 Producer 写人 RocketMQ 的时候,有两种写磁盘方式: 在这里插入图片描述

同步刷盘方式

如上图所示,只有在消息真正持久化至磁盘后,RocketMQ的Broker端才会真正地返回给Producer端一个成功的ACK响应。同步刷盘对MQ消息可靠性来说是一种不错的保障,但是性能上会有较大影响,一般适用于金融业务应用领域。

RocketMQ同步刷盘的大致做法是,基于生产者消费者模型,主线程创建刷盘请求实例—GroupCommitRequest并在放入刷盘写队列后唤醒同步刷盘线程—GroupCommitService,来执行刷盘动作(其中用了CAS变量和CountDownLatch来保证线程间的同步)。这里,RocketMQ源码中用读写双缓存队列(requestsWrite/requestsRead)来实现读写分离,其带来的好处在于内部消费生成的同步刷盘请求可以不用加锁,提高并发度。

在返回写成功状态时,消息已经被写入磁盘 具体流程是,消息写入内存的 PAGECACHE 后,立刻通知刷盘线程刷盘,然后等待刷盘完成,刷盘线程 执行完成后唤醒等待的线程,返回消息写成功的状态

消息存储时首先将消息追加到内存,再根据配值的刷盘 略在不同时间进行刷写磁盘 如果是同步刷盘,消息追加到内存后,将同步调用 MappedByteB uffer force ()方法;如果是异步刷盘,在消息追加到内存后立刻返回给消息发送端 RocketMQ 使用 个单独的线程按照某个设定的频 执行刷盘操作。

通过在 broker 配置文件中配置 flushDiskType 来设定刷盘方式,可选值为 ASYNC_FLUSH (异步刷盘), SYNC_FLUSH 同步刷盘) 默认为异步。

异步刷盘

能够充分利用OS的PageCache的优势,只要消息写入PageCache即可将成功的ACK返回给Producer端。消息刷盘采用后台异步线程提交的方式进行,降低了读写延迟,提高了MQ的性能和吞吐量。异步和同步刷盘的区别在于,异步刷盘时,主线程并不会阻塞,在将刷盘线程wakeup后,就会继续执行。

在返回写成功状态时 ,消息可能只是被写入了内存的 PAGECACHE ,写操作的返回快,吞吐量大 ;当内存里的消息积累到一定程度时,统一触发写 磁盘动作,快速写入。

过期文件删除

由于RocketMQ操作CommitLog、ConsumeQueue文件是基于文件内存映射机制,并且在启动的时候会将所有的文件加载,为了避免内存与磁盘的浪费、能够让磁盘能够循环利用、避免因为磁盘不足导致消息无法写入等引入了文件过期删除机制。

删除过程分别执行清理消息存储文件( Commitlog )与消息消费 队列文件( ConsumeQueue 文件),消息消费队列文件与消息存储文件 (Commitlog )共用一套过期文件机制。

消费完后的消息去哪里了

消息的存储是一直存在于CommitLog中的。而由于CommitLog是以文件为单位(而非消息)存在的,CommitLog的设计是只允许顺序写的,且每个消息大小不定长,所以这决定了消息文件几乎不可能按照消息为单位删除(否则性能会极具下降,逻辑也非常复杂)。所以消息被消费了,消息所占据的物理空间并不会立刻被回收。

但消息既然一直没有删除,那RocketMQ怎么知道应该投递过的消息就不再投递? 客户端自身维护:客户端拉取完消息之后,在响应体中,broker会返回下一次应该拉取的位置,PushConsumer通过这一个位置,更新自己下一次的pull请求。这样就保证了正常情况下,消息只会被投递一次。

删除策略

RocketMQ 清除过期文件的方法是 :如果非当前写文件在一定时间间隔内没有再次被更新,则认为是过期文件,可以被删除, RocketMQ 不会关注 这个文件上的消息是否全部被消费。默认每个文件的过期时间为 42 小时(不同版本的默认值不同,这里以 4.4.0 为例) ,通过在 Broker 配置文件中 设置 fileReservedTime 来改变过期时间,单位为小时。

触发文件清除操作的是一个定时任务,而且只有定时任务,文件过期删除定时任务的周期由该删除决定,默认每 10s 执行一次。

过期判断

fileReservedTime 文件删除主要是由这个配置属性

文件保留时间。也就是从最后一次更新时间到现在,如果超过了该时间,则认为是过期文件,可以删除。

deletePhysicFilesInterval 删除物理文件的时间间隔(默认是 100MS),在一次定时任务触发时,可能会有多个物理文件超过过期时间可被删除, 因此删除一个文件后需要间隔 deletePhysicFilesInterval 这个时间再删除另外一个文件,由于删除文件是一个非常耗费 IO 的操作,会引起消息插入消 费的延迟(相比于正常情况下),所以不建议直接删除所有过期文件。

destroyMapedFileIntervalForcibly 在删除文件时,如果该文件还被线程引用,此时会阻止此次删除操作,同时将该文件标记不可用并且纪录当前时间戳 destroyMapedFileIntervalForcibly 这个表示文件在第一次删除拒绝后,文件保存的最大时间,在此时间内一直会被拒绝删除,当超过这个时间时,会将引用每次减少 1000,直到引用 小于等于 0 为止,即可删除该文件。

删除条件

  1. 消息文件过期(默认72小时),RocketMQ 通过 deleteWhen 设置一天的固定时间执行一次(默认是凌晨4点),删除过期文件。

  2. 消息文件过期(默认72小时),且磁盘空间达到了水位线(默认75%),删除过期文件。

  3. 磁盘已经达到必须释放的上限(85%水位线)的时候,则开始批量清理文件(无论是否过期),直到空间充足。

注:若磁盘空间达到危险水位线(默认90%),出于保护自身的目的,broker会拒绝写入服务。

请登录后发表评论

    没有回复内容