【RocketMQ】Broker存储机制

54 阅读5分钟

Broker存储机制

主要组成:

  • CommitLog:消息存储文件,顺序存储所有Topic的消息,很快的写入速度。

    • 默认大小为1G
  • ConsumeQueue:消息消费队列,异步将消费者需要消费的数据整理到队列里方便消费者顺序读取

    • 单个ConsumeQueue文件中默认包含30万个条目
    • 组成形式:commit offset+size+tag hashcode
  • IndexFile:消息索引文件,一个hash索引存储key与offset

  • MappedFileQueue:映射文件队列,MappedFile的管理容器

  • MappedFile:内存映射文件

  • TransientStorePool:短暂的存储池

  • checkpoint:记录Comitlog、ConsumeQueue、Index文件的刷盘时间点

  • 事务状态服务:存储每条消息的事务状态。

  • 定时消息服务:每一个延迟级别对应一个消息消费队列,存储延迟队列的消息拉取进度。

文件组织方式:单个文件长度固定,写满后新开文件并以偏移量命名

消息存储流程:

  1. 确认是否可以写入:

    1. 当前broker状态正常,为master且支持写入
    2. Topic长度不超过256,消息长度不超过65535
  2. 如果是延迟消息,将消息的原主题名称与原消息队列ID存入消息属性中,用延迟消息主题SCHEDULE_TOPIC、消息队列ID更新原先消息的主题与队列

  3. 获取CommitLog文件路径,申请锁,串行追加写入并设置消息的存储时间

    1. 给消息创建16字节全局唯一ID,获取该消息在消息队列的偏移量,计算消息的总长度。
    2. 判断CommitLog剩余空间是否够用,不够就创建一个新的
    3. 消息存储到ByteBuffer,创建AppendMessageResult,将消息存储在MappedFile对应的内存映射Buffer中
    4. 根据是同步、异步刷盘方式,将内存中的数据持久化到磁盘
  4. 实时更新消息消费队列与索引文件

    1. ReputMessageService线程负责,消息推送1毫秒后尝试推送消息到消息消费队列和索引文件
    2. ConumeQueue更新:根据消息主题与队列ID,获取对应ConumeQueue文件夹的最后一个文件,将消息偏移量、消息长度、tag hashcode写入到ByteBuffer中,将内容追加到ConsumeQueue的内存映射文件中,异步刷盘。
    3. Index更新:获取或创建IndexFile文件并获取所有文件最大的物理偏移量。如果消息的唯一键不为空,则添加到Hash索引中,以便加速根据唯一键检索消息。构建索引键,RocketMQ支持为同一个消息建立多个索引,多个索引键空格分开。

文件恢复原理

  • 发生时机:broker重启后,CommitLog数据与ConumeQueue、IndexFile不一致
  1. 判断上一次退出是否正常,存在abort文件则是异常退出

  2. 加载延迟队列、Commitlog文件,创建MappedFile对象,加载消息消费队列,遍历消息消费队列根目录,获取该Broker的所有主题,遍历每个主题目录获取该主题下的所有消息消费队列,分别加载每个消息消费队列下的文件,构建ConsumeQueue对象。

  3. 加载存储检测点,读取commitlog文件、Consumequeue文件、Index索引文件的刷盘点

  4. 根据Broker是否是正常停止执行不同的恢复策略:

    1. Broker正常停止再重启:从倒数第三个文件开始进行恢复,如果不足3个文件,则从第一个文件开始恢复。遍历Commitlog文件,每次取出一条消息,如果查找结果为true并且消息的长度大于0表示消息正确,mappedFileOffset指针向前移动本条消息的长度;如果查找结果为true并且消息的长度等于0,表示已到该文件的末尾,如果还有下一个文件,则重置processOffset、mappedFileOffset重复,否则跳出循环;如果查找结构为false,表明该文件未填满所有消息,跳出循环,结束遍历文件。更新MappedFileQueue的flushedWhere与commiteedWhere指针。删除offset之后的所有文件。
    2. Broker异常停止文件恢复:与正常恢复的不同之处:从最后一个文件往前走,找到第一个消息存储正常的文件。如果commitlog目录没有消息文件,如果在消息消费队列目录下存在文件,则需要销毁。

文件刷盘机制

  • 同步刷盘:消息追加到内存映射文件的内存中后,立即将数据从内存刷写到磁盘文件

  • 异步刷盘:

    • 开启 transientStorePoolEnable:消息首先追加到堆外内存,然后提交到与物理文件的内存映射内存中,再flush到磁盘。
    • 未开启 transientStorePoolEnable:消息直接追加到与物理文件直接映射的内存中,然后刷写到磁盘中。

过期文件删除机制

  • 原因:磁盘空间有限,避免不需要的数据浪费空间,需要及时删除过期数据。
  • 过期文件的判定:非当前写文件在一定时间间隔内(默认72h)没有再次被更新,则认为是过期文件,可以被删除,RocketMQ不会关注这个文件上的消息是否全部被消费。
  • 过期文件的清理:每隔10s调度一次cleanFilesPeriodically,检测是否需要清除过期文件。分别执行清除Commitlog文件、ConsumeQueue文件。