rocketmq的broker源码解读四(构建consumeQueue)

接到上边broker源码解读二之2.1.1.1.2)处,即启动reputMessageService线程开始构建consumeQueue,在线程的run方法中每sleep一毫秒就循环执行doReput方法;doReput方法中会以当前reputFromOffset为起点循环读取commitLog消息,构建cq消息,若到了mappedFile文件尾,有下一个文件则读取下一个直到最后一条消息,没有则结束;细节流程如下:

4.1)CommitLog#getData方法

4.1.1)MappedFileQueue#findMappedFileByOffset方法

找到offset所在的mappedFile,原理是定位offset所在mappedFile的物理偏移量,commitLog的每个mappedFile文件的名字是1gb的整数倍,consumeQueue的每个mappedFile文件的名字是600万byte的整数倍;这里的offset是reputFromOffset;
4.1.1.1)判断offset是否超出了范围
拿到mfq中list集合中的第一个mappedFile和最后一个mappedFile,若offset小于第一个mappedFile的名字,大于最后一个mappedFile的名字,则超出了范围,直接返回null;
4.1.1.2)计算offset所在mappedFile的在list中的index
计算公式是offset/mappedFileSize 减去 第一个文件的名字/mappedFileSize等于index,即表示在mfq中集合中的索引;举例如(7.6/1g – 5/1g )=2,之所以要减(5/1g),是因为前边的文件随时存在过期被清掉,首文件的index是在变动的,所以需要减;最后通过index从mfq中集合中取出mappedFile并返回;

4.1.2)MappedFile#selectMappedBuffer方法

先计算reputFromOffset%mappedFileSize的值pos,将其作为selectMappedBuffer方法的入参,表示以pos位点开始,到当前mappedFile有效数据为止,创建一个ByteBuffer类型的切片,这个切片供业务访问数据,实现了netty层面的零拷贝;
4.1.2.1)先判断pos是否处于有效数据区间
一段区间是[0,1000],只有[0,500]中写了数据,此时pos等于600,是获取不到数据的,所以无法进行切片;所以入参pos值应该在[0,wrotePosition)之间,左闭右开;
4.1.2.2)引用计数加1
每进行一次切片,引用计数都会加1;
4.1.2.3)先后执行两次切片操作
先记录wrotePosition - pos的差值为size,第一次切片调用mappedByteBuffer.slice方法得到一块byteBuffer1,设置byteBuffer1的position为入参pos值,再次调用byteBuffer1.slice方法,得到一块新的byteBuffer2,并且设置limit值为size,此时byteBuffer2的区间为[0,size),即为用户访问的区间;最后封装到SelectMappedBufferResult实例中,四个属性依次是fireFromOffset+pos,byteBuffer2,size,当前MappedFile实例;
至此,getData方法调用结束;

4.2)CommitLog#checkMessageAndReturnSize

该方法目的是构建DispatchRequest实例,封装了除消息体之外的字段,会返回四种状态;

4.2.1)size>0,success=true

则数据存在且正常;

4.2.2)size=0,success=true

则表示文件尾,可能存在下一个mappedFile文件;

4.2.3)size=-1,success=false

则表示消息全都结束了,且当前是最后一个mappedFile;

4.2.4)size>0,success=false

则数据存在但不正常,如实际大小不等于第一个字段中标注的大小;

4.3)若是4.2.1情况

4.3.1)先执行doDispatch方法

入参为DispatchRequest实例;
开始根据commitLog的mappedFile中的每条消息,构建consumeQueue,其中又执行DefaultMessageStore#putMessagePositionInfo方法;
4.3.1.1)DefaultMessageStore#findConsumeQueue
入参为当前消息的topic和queueId,则可通过查询consumeQueueTable找到对应的ConsumeQueue实例并返回,需要初始化的则进行初始化;在实例化ConsumeQueue时,入参为当前topic,queueId,存储路径,600万字节,以及当前DefaultMessageStore实例,由于一个broker上
4.3.1.2)putMessagePositionInfoWrapper
拿到当前commitLog的mappedFile中消息对应的ConsumeQueue实例后,执行它的putMessagePositionInfoWrapper方法,往ConsumeQueue实例管理的正在写的mappedFile中追加固定大小为20字节的消息;该方法是一个for循环,最大重试次数为30;
4.3.1.2.1)ConsumeQueue#putMessagePositionInfo
四个重要入参依次是
当前消息在commitLog中的物理偏移量phyOffset;
当前消息的大小size;
tagsCode;
当前消息对应的topic-queueId中写入的消息个数其实也是topicQueueTable表中的value(QUEUEOFFSET占8字节,称为队列逻辑偏移量, QUEUEOFFSET*20则表示在consumeQueue文件内真实的偏移量,QUEUEOFFSET是topicQueueTable中的value,表示topic-queueId所对应的消息个数);
4.3.1.2.1.1)将三个字段放入临时缓存
根据入参,将phyOffset,size,tagsCode放入临时缓存byteBufferIndex中;
4.3.1.2.1.2)MappedFileQueue#getLastMappedFile(expectLogicOffset)
expectLogicOffset为当前消息的队列逻辑偏移量乘以20,表示当前消息在topic-queueId对应的ConsumeQueue实例管理的所有mappedFile文件中的偏移量;拿到expectLogicOffset所在的MappedFile实例,即当前消息对应的topic-queueId对应的正在写的mappedFile;
4.3.1.2.1.3)防止重复构建
当前mappedFile的wrotePosition加上其fileFromOffset得到currentLogicOffset;若上一步的expectLogicOffset小于currentLogicOffset,则发出重复构建的警告,返回true;(fireFromOffset是mappedFile的文件名,表示偏移量)
4.3.1.2.1.4)追加消息
先更新当前ConsumeQueue实例的maxPhysicOffset为phyOffset+size,接着调用MappedFile#appendMessage,入参为byteBufferIndex;其中又会调用fileChannel.position(wrotePosition)调整位点,再调用fileChannel.write写入数据;最后更新当前mappedFile的wrotePosition,即增加data.length;
4.3.1.2.2)设置存储时间戳
退出putMessagePositionInfoWrapper方法;若上一步返回false,则再次尝试,最大尝试30次;
至此doDispatch方法执行完毕,完成了commitLog消息构建成consumeQueue消息,构建的本质是将commitLog对应的所有mappedFile中的每条消息按照topic-queueId分类,只提取三个字段(phyOffset,size,tagsCode)组成固定20字节的新消息存进新的mappedFile文件中,每个topic-queueId下的所有mappedFile文件放进list集合中,由mappedFileQueue管理,再有ConsumeQueue管理,即一个topic-queueId对应一个ConsumeQueue实例;

4.3.2)处理长轮询

若当前机器是主节点,并开启了长轮询,则调用NotifyMessageArrivingListener#arriving方法,其中会调用PullRequestHoldService#notifyMessageArriving方法;最后更新reputFromOffset+=size,readSize+=size;再次循环,从4.2开始执行,操作下一条消息;

4.4)若是4.2.2情况

则执行CommitLog#rollNextFile方法,更新reputFromOffset为下一个mappedFile的名字,更新readSize;此时跳出内层循环,再次从4.1)开始执行,拿到下一个mappedFile的切片,取出每一条消息构造DispatchRequest实例,再以此实例为基础构建consumeQueue消息;若外层循环判断reputFromOffset等于commitLog的消息的最大物理偏移量,则释放切片资源,退出外层循环,构建结束;

4.5)若是4.2.4情况

报错,更新reputFromOffset;继续判断下一条消息;

4.6)若是4.2.3情况

则表示消息全都结束了,此时跳出内层,释放切片资源,跳出外层循环,结束方法;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

orcharddd_real

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值