1.RocketMQ存储概要设计
RocketMQ主要存储的文件包括Comitlog文件、ConsumeQueue文件、IndexFIle文件。RocketMQ将所有主题的消息存储在同一个文件中,确保消息发送时顺序写文件,尽最大的能力确保消息发送的高性能与吞吐量。但由于消息中间件一般是订阅机制,这样便给按照消息主题检索带来了极大的不便。为了提高效率,RocketMQ引入了ConsumeQueu消息队列文件,每个消息主题包含多个消息消费队列,每一个消息队列有一个消息文件。IndexFile索引文件,主要设计理念就是加速消息的检索性能,根据消息的属性快速从CommitLog文件中检索消息
1.1: commitLog:消息存储文件,所有消息主题的消息都存储在CommitLog文件中。
1.2 ConsumeQueue:消息消费队列,消息到达CommitLog文件后,将异步转发到消息消费队列,供消息消费者消费。
1.3 IndexFile:消息索引文件,主要存储消息key与Offset的对应关系
1.4 事物状态服务:存储每条消息的事物状态
1.5 定时消息服务:每一个延迟级别对应一个消息消费队列,存储延迟队列的消息拉取进度。
2.功能描述
Broker是处理消息存储,转发等处理的服务器。
1.Broker以group分开,每个group只允许一个master,若干个slave。
2.只有master才能进行写入操作,slave不允许。
3.slave从master中同步数据,同步策略取决于master的配置,可以采用同步双写,异步复制两种。
4.客户端消费可以从master和slave消费。在默认情况下,消费者都从master消费,在master挂后,客户端由于从Name Server
中感知到Broker挂掉,就会从salve消费。
5.Broker向所有的Name Server节点建立长链接,注册Topic 信息。
3.消息发送存储流程
代码结构:
消息存储实现类:
在store模块中:
org.apache.rocketmq.store.DefaultMessageStore
这个类在存储模块中最重要的一个类:
下面列举一下核心属性和方法
1. MessageStoreConfig messageStoreConfig :消息存储配置属性
2.CommitLog commitLog:CommitLog文件的存储实现类
3.ConcurrentMap<String /**topic */,ConcurrentMap<Integer/*queueId*/,Conssume-Queue>> consumeQueueTable:消息队列存储缓存表,按消息主体分组。
4.FlusConsumeQueueService flushConsumeQueueService :消息队列文件ConsumeQueue刷盘线程。
5. CleanCommitLogService cleanCommitLogService:清除CommitLog文件服务
6.CleanConsumeQueueService cleanConsumeQueueService :清除ConsumeQueue文件服务
7.IndexService indexService :索引文件实现类
8.AllocateMappedFileSErvice allocateMApppedFileService:MappedFile分配服务。
9.ReputMessageServie reputMessageService: CommitLog消息分发,根据CommitLog文件构建ConsumeQueue、IndexFile文件
10.HAService haService:存储HA机制
11.TransientStorePool transientStorePool: 消息堆内存缓存
12.MessageArrivingListener messagArrivingListener: 消息拉取长轮训模式消息达到监听器。
13.BrokerConfig brokerConfig :Broker配置属性。
14.StoreCheckpoint storeCheckPoint:文件刷盘监测点
15.LinkedList<CommitLogDispacher> dispatcherList:CommitLog文件转发请求。
消息存储追踪入口:
org.apache.rocketmq.store.DefaultMessageStore#putMessage
注意事项:
master节点允许写入,消息主体长度不能超过256个字符,消息属性成都不能超过65536个字符。
第二步:
如果消息的延迟级别大于0,将消息的原主题名称与原消息队列ID存入消息属性中,用延迟消息主体SCHEDULE_TOPIC,消息队列ID更新原消息的主题与队列,这个是并发消息消费重试关键的一步。
代码中有关于延迟时间的判断
第三步:
获取当前可以写入Commitlog文件,RocketMQ物理文件如下图:
Commitlog文件存储目录为${ROCKETMQ_HOME}/store/commitlog目录,mac系统下面可以在用户根目录下面找store文件夹
每个文件默认1G,一个文件写满后,会再创建另外一个,以该文件中第一个偏移量为文件名称,偏移量小于20位用0补齐。如上图第一个文件的偏移量为0。MappedFileQueue可以看做是${ROCKETMQ_HOME}/store/commitlog文件夹,而MappedFile则对应该文件夹下一个个的文件。
第四步:
在写入Commitlog之前,先申请锁,消息存储到CommitLog文件是串行的。
第五步:
设置消息的存储时间,如果mappedFile为空,表明${ROCKETMQ_HOME}/store/commitlog目录下不存在任何文件,说明本次消息是第一次消息发送,用偏移量0创建第一个commit文件,。文件名:00000000000000000000
第六步:
将消息追加到MappedFile中。
第七步:
创建全局唯一消息ID,消息ID有16字节
4字节IP:4字节端口号:8字节消息偏移量
第八步:
获取改消息在消息队列的偏移量。CommitLog中保存了当前所有消息队列的当前待写入偏移量。
第九步:
根据消息体的长度、主体的长度、属性的长度结合消息存储格式计算消息的总长度。
第十步:
如果消息长度+END_FILE_MIN_BLAK_LENGTH大于CommitLog文件的空闲空间,则返回AppendMessageStatus.END_OF_FILE,Broker会重新创建一个新的CommitLog文件来存储该消息。从这里可以看出,每个CommitLog文件最少会空闲8个字节,高4字节存储当前文件剩余空间,低4字节存储魔数:CommitLog.BLANK_MAGIC_CODE
十一步:
将消息内容存储到ByteBuffer中,然后创建AppendMessageResult。这一步只是将消息存储在MappedFile对应的内存映射Buffer中,并没有刷写到磁盘。
十二步:
更新消息队列逻辑偏移量
十三步:
处理完消息追加逻辑后将释放putMessageLock锁。
后续会分析:存储文件组织与内存映射机制
参考资料:《RocketMQ技术内幕》