ROCKETMQ消息存储
ROCKETMQ存储采用存储文件的方式,使用netty作为通信框架,消息生产者发送消息后,将报文解析为RemotingCommand类,然后按照顺序进行消息存储。同时将消息的物理偏移量记录到messageId中,同时会生成索引文件:consumerQueue,indexFile,这两个文件中记录消息的物理偏移量,方便查账消息
查找消息时,都是根据索引文件记录的消息偏移量进行查找。
主要存储文件
- commitlog:消息内容存储文件。消息到达broker后进行存储
路径:ROCKET_HOME/store/commitlog/* - consumeQueue:消息消费队列存储文件,消息存储到commitlog文件后,将会异步转发到该文件,主要用于消息消费根据topic拉取文件。也支持根据topic和queueid以及时间戳进行查找
路径:ROCKET_HOME/store/consumequeue/tipic/queueid/* - indexFile:消息索引文件,发送消息是设置的key会添加到此文件进行索引,方便根据key,快速找到消息
存储流程
- 接收到生产者的请求后,解析为 MessageExtBrokerInner 类,执行CommitLog.putMessage方法进行存储消息,从当前的wrotePosition 位置开始,按照一定格式存储进最后一个 commotLog文件中(默认1G)。
- 同时异步将commotLog中新增的消息转发到ConsumeQueue文件和indexFile文件中。
- commitLog消息格式: 总大小(4)+魔数(4)+CRC校验码(4)+队列id(4)+FLAG(4)+队列偏移量(8)+物理偏移量(8)+SYSFLAG(4)+生产时间戳(8)+消息发送者ip(8)+存储时间戳(8)+存储服务ip,port(8)+消息重试次数(4)+事务消息物理偏移量(8)+消息长度(4)+消息内容(上一个值)+TOPIC长度(1)+TOPIC(上一个值)+消息属性长度(2)+消息属性(上一个值)
- consumeQueue文件格式:指向commitLog的消息物理偏移量(8)+消息总大小(4)+tag hashCode(8)
- indexFile 文件格式 :40文件属性信息+500W个hash槽+2000W个消息index。hash槽格式为: index的位置(4);index条目格式为: key hashCode(8)+消息物理偏移量(8)+和当前文件第一条消息的时间戳差值(4)+上一个消息index的位置(4)
- 为什么index笔hash槽多个,因为要处理hash槽冲突的情况,一个hash槽可能会对应多个index条目。每次通过hash槽的值找到index条目都要对比hashCode是否一致,同时循环通过上一个消息的index位置继续找,直到没有上一个。同时通过物理偏移量找到真实消息信息后还要再次校验key原值,防止hash值冲突导致找错信息。
- 代码入口:
org.apache.rocketmq.broker.processor.SendMessageProcessor#asyncProcessRequest
SEND_MESSAGE_V2 格式是为了缩短报文长度。
其他文件
- checkpoint 文件记录了上面三种文件最后一次的刷盘时间点。
- config/* 记录了每个topic 的配置信息
- abort 文件代表文件是否正常关闭,正常启动是生成,正常关闭是删除。
- 如果当启动时abort 文件存在,则代表上一次是非正常关闭
启动时加载文件
- 首先加载所有commitlog 文件
- 加载所有consumeQueue文件
- 加载所有indexFile文件
- 判断abort 文件是否正常,正常关闭时,会校验倒数三个 commitlog文件里的所有消息crc,将最后一个校验通过的消息之后的文件和内容全部清除(防止发送错误消息)。如果非正常关闭,则校验最后一个commitlog文件里的所有消息是否正常,将正常的消息转发到 consumequeue和indexfile文件,同时将最后一个校验通过消息之后的内容全部清除。
文件持久化
MappedFile 里有几个重要参数分别是:writeBuffer :堆外内存,transientStorePool:堆外内存池,就是writeBuffer从这里获取,用完再归还,mappedByteBuffer:物理文件的映射内存。
- 同步刷盘:每次写入的消息都直接提交到 物理文件的映射内存,同时force()刷到物理文件。
- 异步刷盘
参数 transientstorepooleenable为true且为异步刷盘
则会开启堆外内存缓冲区,目的是减少刷盘频率;每次写入消息都会写入writeBuffer堆外内存,它会每200ms 将缓冲区的内存刷到物理文件的映射内存,但是限制每次必须提交 4页的内容(16K),不足4页就会限制频率。同时每500ms会将 文件的映射内存 force刷到物理文件。
参数 transientstorepooleenable为false,则不开启 堆外内存缓冲区,每次写入消息都会直接写入 物理文件的映射内存,每500ms将映射内存 force刷到物理文件。
- rocketmq默认为异步刷盘,每200ms 将缓冲区的内容刷到 映射内存,每500ms将映射内存的内容刷到物理文件
过期文件清除
rocketmq 每10s检查一次过期文件,超过72小时没有更新即为过期文件,会启动删除任务,首先会检测文件是否有引用,如果有则拒绝删除,同时记录当前时间戳(目的是超过一定时间,即使有引用也要强制删除),在此期间,每次拒绝都会减少1000引用,直到引用为0或超过最大存活时间被强制删除。