前面提到RocketMQ是基于Topic模型的,这里先简单了解一下broker中是如何存储消息的。

首先要知道的,RocketMQ的消息存储是基于文件系统的,主要包含三类文件:CommitLog、ConsumerQueue、IndexFile。

其中:

  • CommitLog 消息主体以及元数据的存储主体。 所有producer端写入的消息主体内容都会写入到CommitLog文件中。虽然消息内容的长度不是固定长度的,但是对于CommitLog,每个CommitLog文件是固定长度的,大小是1G(这么做的主要目的是充分利用PageCache特性,提供文件的读性能)。CommitLog的文件名由固定的规则,长度20位,左侧补0,剩余位为起始偏移量,比如00000000000000000000代表第一个文件, 起始偏移量为0,1G=1073741824,当第一个文件写满时,第二个文件的文件名为:00000000001073741824,起始偏移量为1073741824,以此类推。 消息是按顺序写入到文件中,当文件满了,写入到下一个文件。
  • ConsumeQueue 消息消费队列。这是一个逻辑消费队列,可以作为消费消息的索引,保存了指定Topic下的队列消息在CommitLog中的起始物理偏移量offset、消息大小size和消息tag的hashcode值。ConsumeQueue可以看成是基于Topic的CommitLog的索引文件,故ConsumerQueue文件的组织形式如:$HOME/store/consumequeue/{topic}/{queueId}/{fileName}。另外,需要说明的是,ConsumerQueue文件采用定长设计,文件中每个条目20个字节(8字节的 commitlog物理偏移量、4字节的消息长度、8字节tag hashcode),单个文件30W个条目,由于每个条目都是固定长度的,因此可以实现随机访问每个条目。 每个ConsumerQueue文件的大小约为5.72M
  • IndexFile 索引文件。 主要是用于提供基于时间、key、topic的消息查询。在MQ系统中提供管理查询的能力,非主体功能,这里暂不讨论。

总结来说,整个消息存储的结构,最主要的就是 CommitLog和 ConsumeQueue。 其中CommitLog用于存储消息内容。ConsumeQueue通过Topic和QueueId组织文件,内容条目用于索引具体的消息。如图:

rocketmq java 客户端如何配置accessKey 和 secretKey rocketmq_client log_偏移量

消息存储过程:

  1. CommitLog消息存储
    时序图:

rocketmq java 客户端如何配置accessKey 和 secretKey rocketmq_client log_java_02

  1. ConsumerQueue存储
    当Broker收到新的消息时,首先先将消息写入CommitLog中,然后再写ConsumerQueue,RocketMQ早期版本采用同步方式,较新版本采用异步方式,即起一个线程, 不停的轮询, 将当前的consumeQueue中的offSet和commitLog中的offSet进行对比, 将多出来的offSet进行解析, 然后put到consumeQueue中的MapedFile中。这样做的好处应该是为了增加发送消息的吞吐量。
  2. 刷盘策略
    调用MapedFile的appendMessage后, 也只是将消息装载到了ByteBuffer中, 也就是内存中, 还没有落盘. 落盘需要将内存flush到磁盘上, 针对commitLog, rocketMQ提供了异步落盘和同步落盘两种落盘方式.
总结

RocketMQ对消息的存储结构进行了很多优化。

Broker这边主要的工作是1、收到消息后如何存储 2、消息如何消费。 而存储结构贯穿整个过程。 后面我们分析源码时就容易理解一些了。