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会拒绝写入服务。


文章作者: fFee-ops
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 fFee-ops !
评论
  目录