【mq读书笔记】mq消息存储
- comitlog文件
- ConsumerQueue文件
- IndexFile文件
RocketMQ将所有主题的消息存储在同一个文件中,确保消息发送时顺序写文件。
为了提高消息消费的效率RocketMQ引入了ConsumeQueue消息队列文件,每个消息主题包含一个多个消息消费队列,每一个消费队列有一个消费文件。消息到达CommitLog文件后,将异步转发到消息消费队列
为了加速消息的检索性能,引入IndexFile索引文件,根据消息的属性快速从Commitlog文件中检索消息。存储消息Key与Offset的对应关系。
- messageStoreConfig消息存储配置属性
- commitLog文件的存储实现类
- consumeQueueTable消息队列缓存列表,按消息主题分组
- flushConsumeQueueService消息队列文件ConsumeQueue刷盘线程
- cleanCommitLogService清除CommitLog文件服务
- cleanConsumeQueueService 清除ConsumeQueue文件服务
- indexService 索引文件实现类
- allocateMappedFileService MappedFile分配服务
- reputMessageService commitLog消息分发,根据CommitLog文件构建ConsumeQueue,IndexFile。
- haService存储HA机制
- transientStorePool消息堆内存缓存
- MessageArrivingListener 消息拉去长轮询模式达到监听器
- brokerConfig。Broker配置属性
- storeCheckpoint 文件刷盘检查点
- dispatcherList。CommitLog文件转发请求
设置消息存储的时间,如果mappedFile为空表明commitlog目录下不存在任何文件,说明本次消息是第一次消息发送,用偏移量0创建第一个commit文件,如果文件创建失败,抛出CREATE_MAPEDFILE_FAILED,很有可能是磁盘空间不足或权限不够
msgId=4字节IP+4字节端口号+8字节消息偏移量
消息存储格式(略)
至此消息已经刷到内存中,返回的结果如下
若此时将put_ok作为结果返回(异步刷盘,)则客户端收到消息成功的结果之后,服务器宕机没刷到磁盘的消息会丢失。
再看PutMessageResult返回之前的刷盘:
同步刷盘的情况下 会因为等待刷盘超时将PUT_OK更新成FLUSH_DISK_TIMEOUT,默认情况为 ASYNC_FLUSH 修改flushDiskType = SYNC_FLUSH还用同步刷盘。
进一步看下消息主从同步之后的结果:
可以看slave不可用的时候会更新为SLAVE_NOT_AVAILABLE或者FLUSH_NOT_AVAILABLE
此时若master宕机,没有同步到slave的消息就会丢失
默认为 ASYNC_MASTER 修改brokerRole=SYNC_MASTER 改用同步复制。
但是以上都不会再更新AppendMessageResukt,最后返回给客户端都会被认为消息发送成功的
org.apache.rocketmq.broker.processor.SendMessageProcessor#handlePutMessageResult:
再看下客户端producer怎么处理这几种情况的:
可以看到当sendStaus!=OK的时候 会根据设置进行重试,但在重试达到最大次数 默认2,就会直接将结果sendResult返回给业务调用方:
综上说书,如果要保证严格的不丢消息的情况:
broker 需要采用如下配置:

同时这个过程我们还需要生产者配合,判断返回状态是否是 SendStatus.SEND_OK。若是其他状态,就需要考虑补偿重试。
虽然上述配置提高消息的高可靠性,但是会降低性能,生产实践中需要综合选择