Broker存储机制
主要组成:
-
CommitLog:消息存储文件,顺序存储所有Topic的消息,很快的写入速度。
- 默认大小为1G
-
ConsumeQueue:消息消费队列,异步将消费者需要消费的数据整理到队列里方便消费者顺序读取
- 单个ConsumeQueue文件中默认包含30万个条目
- 组成形式:commit offset+size+tag hashcode
-
IndexFile:消息索引文件,一个hash索引存储key与offset
-
MappedFileQueue:映射文件队列,MappedFile的管理容器
-
MappedFile:内存映射文件
-
TransientStorePool:短暂的存储池
-
checkpoint:记录Comitlog、ConsumeQueue、Index文件的刷盘时间点
-
事务状态服务:存储每条消息的事务状态。
-
定时消息服务:每一个延迟级别对应一个消息消费队列,存储延迟队列的消息拉取进度。
文件组织方式:单个文件长度固定,写满后新开文件并以偏移量命名
消息存储流程:
-
确认是否可以写入:
- 当前broker状态正常,为master且支持写入
- Topic长度不超过256,消息长度不超过65535
-
如果是延迟消息,将消息的原主题名称与原消息队列ID存入消息属性中,用延迟消息主题SCHEDULE_TOPIC、消息队列ID更新原先消息的主题与队列
-
获取CommitLog文件路径,申请锁,串行追加写入并设置消息的存储时间
- 给消息创建16字节全局唯一ID,获取该消息在消息队列的偏移量,计算消息的总长度。
- 判断CommitLog剩余空间是否够用,不够就创建一个新的
- 消息存储到ByteBuffer,创建AppendMessageResult,将消息存储在MappedFile对应的内存映射Buffer中
- 根据是同步、异步刷盘方式,将内存中的数据持久化到磁盘
-
实时更新消息消费队列与索引文件
- ReputMessageService线程负责,消息推送1毫秒后尝试推送消息到消息消费队列和索引文件
- ConumeQueue更新:根据消息主题与队列ID,获取对应ConumeQueue文件夹的最后一个文件,将消息偏移量、消息长度、tag hashcode写入到ByteBuffer中,将内容追加到ConsumeQueue的内存映射文件中,异步刷盘。
- Index更新:获取或创建IndexFile文件并获取所有文件最大的物理偏移量。如果消息的唯一键不为空,则添加到Hash索引中,以便加速根据唯一键检索消息。构建索引键,RocketMQ支持为同一个消息建立多个索引,多个索引键空格分开。
文件恢复原理
- 发生时机:broker重启后,CommitLog数据与ConumeQueue、IndexFile不一致
-
判断上一次退出是否正常,存在abort文件则是异常退出
-
加载延迟队列、Commitlog文件,创建MappedFile对象,加载消息消费队列,遍历消息消费队列根目录,获取该Broker的所有主题,遍历每个主题目录获取该主题下的所有消息消费队列,分别加载每个消息消费队列下的文件,构建ConsumeQueue对象。
-
加载存储检测点,读取commitlog文件、Consumequeue文件、Index索引文件的刷盘点
-
根据Broker是否是正常停止执行不同的恢复策略:
- Broker正常停止再重启:从倒数第三个文件开始进行恢复,如果不足3个文件,则从第一个文件开始恢复。遍历Commitlog文件,每次取出一条消息,如果查找结果为true并且消息的长度大于0表示消息正确,mappedFileOffset指针向前移动本条消息的长度;如果查找结果为true并且消息的长度等于0,表示已到该文件的末尾,如果还有下一个文件,则重置processOffset、mappedFileOffset重复,否则跳出循环;如果查找结构为false,表明该文件未填满所有消息,跳出循环,结束遍历文件。更新MappedFileQueue的flushedWhere与commiteedWhere指针。删除offset之后的所有文件。
- Broker异常停止文件恢复:与正常恢复的不同之处:从最后一个文件往前走,找到第一个消息存储正常的文件。如果commitlog目录没有消息文件,如果在消息消费队列目录下存在文件,则需要销毁。
文件刷盘机制
-
同步刷盘:消息追加到内存映射文件的内存中后,立即将数据从内存刷写到磁盘文件
-
异步刷盘:
- 开启 transientStorePoolEnable:消息首先追加到堆外内存,然后提交到与物理文件的内存映射内存中,再flush到磁盘。
- 未开启 transientStorePoolEnable:消息直接追加到与物理文件直接映射的内存中,然后刷写到磁盘中。
过期文件删除机制
- 原因:磁盘空间有限,避免不需要的数据浪费空间,需要及时删除过期数据。
- 过期文件的判定:非当前写文件在一定时间间隔内(默认72h)没有再次被更新,则认为是过期文件,可以被删除,RocketMQ不会关注这个文件上的消息是否全部被消费。
- 过期文件的清理:每隔10s调度一次cleanFilesPeriodically,检测是否需要清除过期文件。分别执行清除Commitlog文件、ConsumeQueue文件。