【RocketMQ源码分析】深入消息存储(2)

前文回顾
CommitLog篇 ——【RocketMQ源码分析】深入消息存储(1)
MappedFile篇 ——【RocketMQ源码分析】深入消息存储(3)
前文说完了一条消息如何被持久化到本地磁盘CommitLog,本篇就要谈谈如何从CommitLog来构建我们消息消费的核心队列结构ConsumeQueue了。
之前已经说过,CommitLog文件是消息的大杂烩,所有消息具体都被放到了这个大文件中,而ConsumeQueue则是一个逻辑上的队列,也是消息消费的核心,它存在Topic与Queue这两个概念,也就是消费者在消费时需要关心的东西。

除了ConsumeQueue目录下存在Topic与QueueId组成的两级目录,它实际存储消息的文件也是与CommitLog相似,是一个文件名代表offset的文件。
在ConsumeQueue中,每一个单元结构如下图:

CommitLog Offset : 8 Byte
Size : 4 Byte
Message Tag Hashcode : 8 Byte
第一个8Byte的CommitLog Offset代表该消息在CommitLog文件中的偏移位置。
第二个4Byte的Size代表消息的大小。
凭借CommitLog Offset与Size就可以在CommitLog中定位一条消息。
而第三个字段Message Tag Hashcode则是用来快速过滤消息。
可以发现,ConsumeQueue中每个消息占据20Byte,也就是说如果MessageStoreConfig的mappedFilesizeConsumeQueue设置为100Byte,那么每个ConsumeQueue文件只能存储5条消息。
了解完ConsumeQueue,接下来就需要知道ConsumeQueue是在什么时候构建的,在上一篇CommitLog存储消息时,我们没有看到有写入ConsumeQueue,那是因为ConsumeQueue是由一个异步线程去构建的。
异步构建流程如下,DefaultMessageStore的内部类ReputMessageService继承自ServiceThread,其run方法中每1毫秒调用一次doReput方法。

reputFromOffset是初始开始同步的偏移位置,默认为0。
在doReput方法中进行了ConsumeQueue的构建。
首先,是一个只有在异常情况下才会终止的for循环,doNext只有返回值错误的情况下才会设为false,可以理解为一个死循环。

如果当前reput的偏移量大于等于已确认的,就不需要构建。

如果符合条件,就从CommitLog中读取对应位置的数据,如果返回null,就终止当前for死循环,不为null则去构建ConsumeQueue。

可以看看result返回了什么。

如果result不为null,获取起始偏移位置,并且设置为doReput的reputFromOffset偏移量。

之后进入checkMessageAndReturnSize方法,该方法就是从result变量的buffer中读取消息的具体数据了,一堆buffer.get操作,此处不列出了,感兴趣可以看CommitLog#checkMessageAndReturnSize方法。

方法最后包装了一个DispatchRequest对象返回。

可以看看此处Debug拿到的DispatchRequest,主要有Topic名称,在CommitLog中的偏移位置,以及消息大小,如果你还记得上一篇CommitLog在最后判断CommitLog文件能不能存下这条消息时,可以看到当时就是一个121字节加8字节魔术大小的消息,在此处成功读取到了。

读取成功之后,就要构建ConsumeQueue了。

进入doDispatch方法,可以看到要构建的目标是有一个list,是CommitLogDispatcher的子类做了实现。

可以看到下图,实现有ConsumeQueue和Index两个,也就是说ConsumeQueue和Index文件的异步构建都是在此处,两个类都位于DefaultMessageStore中。


ConsumeQueue是逻辑消息队列,Index则是消息索引文件,存储了消息的存储时间,哈希,偏移量等信息,本篇主要看ConsumeQueue,所以走到CommitLogDispatcherBuildConsumeQueue的实现即可。

首先获取了消息类型,进入putMessagePositionInfo方法。

putMessagePositionInfo中分为两步,首先拿到ConsumeQueue对象,然后放入请求。
第一步的findConsumeQueue,可以根据topic和queueId来定位一个ConsumeQueue,正如我们之前看到了目录结构,这个获取的流程是先查找map,这个没有就新建。
步骤如下图,首先从table中拿到ConsumeQueue,如果没有拿到,就新建一个key是指定topic的结构,添加进table。

consumeQueueTable结构如下,是一个ConcurrentMap<String, ConcurrentMap<Integer,ConsumeQueue>>。

拿到consumeQueueTable内置的map之后,就可以根据QueueId拿到目标ConsumeQueue了。

如果有,直接返回,如果没有,就说明需要初始化该ConsumeQueue了,初始化完成之后放入consumeQueueTable,并且返回该ConsumeQueue。

拿到ConsumeQueue就可以对请求进行构建了,进入putMessagePositionInfoWrapper方法。
首先获取了当前Store状态是否可写,如果可写,会在for循环内尝试maxRetries次构建,该值是30次。

在for循环内,首先拿到了tag,然后在isExtWriteEnable处判断配置是否启用了写入扩展信息,如果开启,会在一个ConsumeQueueExt的结构中写入存储时间、Tag、bitMap等信息,此处先跳过。
此时调用了putMessagePositionInfo方法写入,如果返回result成功的话,直接return,否则休眠一秒钟,然后继续for循环,根据maxRetries的值,可以有30次机会。

进入putMessagePositionInfo方法,可以看到向ConsumeQueue的byteBufferIndex中put了数据,该buffer是何时初始化的呢?

ConsumeQueue的构造函数中,就对byteBufferIndex进行了初始化

public static final int CQ_STORE_UNIT_SIZE = 20;
可以看到buffer的大小只有20字节,符合我们之前说的每个消息在ConsumeQueue中的大小。
而putMessagePositionInfo中首先也对byteBufferIndex进行了flip操作,也就是读写模式的切换,此处切换到了写模式,并且将目标数据放入buffer中。
之后就是熟悉的MappedFile操作,可以参考上一篇CommitLog中讲述的MappedFile。
最后appendMessage将消息写入文件channel中,等待刷盘时持久化。

以上就是ConsumeQueue的构建流程了。
【RocketMQ源码分析】深入消息存储(2)的更多相关文章
- 【RocketMQ源码分析】深入消息存储(3)
前文回顾 CommitLog篇 --[RocketMQ源码分析]深入消息存储(1) ConsumeQueue篇 --[RocketMQ源码分析]深入消息存储(2) 前面两篇已经说过了消息如何存储到Co ...
- RocketMQ 源码分析 —— Message 发送与接收
1.概述 Producer 发送消息.主要是同步发送消息源码,涉及到 异步/Oneway发送消息,事务消息会跳过. Broker 接收消息.(存储消息在<RocketMQ 源码分析 —— Mes ...
- RocketMQ源码分析之从官方示例窥探:RocketMQ事务消息实现基本思想
摘要: RocketMQ源码分析之从官方示例窥探RocketMQ事务消息实现基本思想. 在阅读本文前,若您对RocketMQ技术感兴趣,请加入RocketMQ技术交流群 RocketMQ4.3.0版本 ...
- 【RocketMQ源码分析】深入消息存储(1)
最近在学习RocketMQ相关的东西,在学习之余沉淀几篇笔记. RocketMQ有很多值得关注的设计点,消息发送.消息消费.路由中心NameServer.消息过滤.消息存储.主从同步.事务消息等等. ...
- RocketMQ源码分析之RocketMQ事务消息实现原理上篇(二阶段提交)
在阅读本文前,若您对RocketMQ技术感兴趣,请加入 RocketMQ技术交流群 根据上文的描述,发送事务消息的入口为: TransactionMQProducer#sendMessageInTra ...
- ROCKETMQ源码分析笔记1:tools
rocketmq源码解析笔记 大家好,先安利一下自己,本人男,35岁,已婚.目前就职于小资生活(北京),职位是开发总监. 姓名DaneBrown 好了.我保证本文绝不会太监!转载时请附上以上安利信息. ...
- RocketMQ源码分析 broker启动,commitlog、consumequeue、indexfile、MappedFileQueue、MappedFile之间的关系以及位置说明
目录 1.MappedFile类属性说明 1.1.MappedFile类属性如下 1.2.MappedFile构造器说明 2.MappedFileQueue类说明 2.1.属性说明 2.2.Mappe ...
- ROCKETMQ源码分析笔记2:client
CLIENT 之前讲过tools里面有大量调用client的东西.为了从源码层面了解rocket,决定啃下client这块骨头. pom 先看pom,看看CLIENT依赖谁.看完后原来是依赖commo ...
- RocketMQ 源码分析之路由中心(NameServer)
你可能没有看过 RocketMQ 的架构图,没关系,一起来学习一下,RocketMQ 架构图如下: 在 RocketMQ 中,有四个角色: Producer:消息的生产者,每个 MQ 中间件都有. C ...
随机推荐
- Base 64 & URL & blob & FileReader & createObjectURL
Base 64 & URL & blob & FileReader & createObjectURL /** * let blob = item.getAsFile( ...
- NGK” 呼叫河马 “智能合约火爆全网
最近有一款基于NGK.IO公链上的智能合约"呼叫河马"在区块链市场很火.通过访问和查阅资料可知,"呼叫河马"是一款全新的智能合约Dapp小游戏,智能合约代码是1 ...
- 若依管理系统RuoYi-Vue(二):权限系统设计详解
若依Vue系统中的权限管理部分的功能都集中在了系统管理菜单模块中,如下图所示.其中权限部分主要涉及到了用户管理.角色管理.菜单管理.部门管理这四个部分. 一.若依Vue系统中的权限分类 根据观察,若依 ...
- 远程过程调用框架——gRPC
gRPC是一款基于http协议的远程过程调用(RPC)框架.出自google.这个框架可以用来相对简单的完成如跨进程service这样的需求开发. 资料参考: https://blog.csdn.ne ...
- idea中Maven-build lifecycle中下面标签详解
原文链接:https://blog.csdn.net/mr_orange_klj/article/details/82153945 Maven是基于一个build lifecycle的中心概念,意味着 ...
- Vue学习笔记-vue-element-admin 按装报错再按装
一 使用环境 开发系统: windows 后端IDE: PyCharm 前端IDE: VSCode 数据库: msyql,navicat 编程语言: python3.7 (Windows x86- ...
- CentOS7安装ElasticSearch7.9.2
1:下载 wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.9.2-linux-x86_64.tar. ...
- WPF -- 一种实现本地化的方法
本文介绍一种WPF程序实现本地化的方法. 步骤 首先,假设xaml文件中存在一个Button按钮,内容为"按钮",实现本地化的步骤如下: 展开程序的Properties,双击Res ...
- CCF(棋局评估)博弈论+对抗搜索+DFS
201803-4 棋局评估 这题主要使用对抗搜索,也就是每一步寻找可以下棋的位置,通过在这一步下棋看最后会取的什么样的分数. #include<iostream> #include< ...
- POJ-3159(差分约束+Dijikstra算法+Vector优化+向前星优化+java快速输入输出)
Candies POJ-3159 这里是图论的一个应用,也就是差分约束.通过差分约束变换出一个图,再使用Dijikstra算法的链表优化形式而不是vector形式(否则超时). #include< ...