rocketmq的broker源码解读三(broker存储)

3)broker执行存储

3.1)生产者发送存储请求

一般生产者的发送批量消息时,编码为RequestCode.SEND_BATCH_MESSAGE,发送非批量消息时编码为RequestCode.SEND_MESSAGE_V2,这里只研究非批量;

3.2)broker端io处理

3.3)broker端业务处理入口

3.3.1)NettyServerHandler#channelRead0方法

broker收到消息后会执行NettyRemotingServer# NettyServerHandler# channelRead0方法,其中会执行NettyRemotingAbstract#processMessageReceived;其中会选择请求类型为REQUEST_COMMAND,执行其对应的NettyRemotingAbstract#processRequestCommand方法;该方法中先根据RequestCode从processorTable中取出对应的Pair实例,再拿到Pair实例中的processor,关于processRequestCommand方法的其他细节在namesrv中已讲过;

3.3.2)同步或者异步存储

接着执行sendMessageProcessor#processRequest方法,其中又会执行sendMessageProcessor#asyncProcessRequest方法,其中又会执行sendMessageProcessor#asyncSendMessage方法,这里接下来分两种执行策略,同步或者异步;
异步处理会执行DefaultMessageStore#asyncPutMessage(MessageExtBrokerInner msg)方法,同步处理会执行MessageStore#asyncPutMessage(MessageExtBrokerInner msg)方法;这里只研究非批量的同步处理;其中又会执行DefaultMessageStore#putMessage方法,中又会执行CommitLog#putMessage(final MessageExtBrokerInner msg)方法;

3.4)存储的核心方法CommitLog#putMessage方法

3.4.1)进行一些检查

是否为shutdown,是否为slave,磁盘是否满了,若有一个是则return PutMessageStatus.SERVICE_NOT_AVAILABLE;
判断刷盘是否繁忙,若是的,则return PutMessageStatus.OS_PAGECACHE_BUSY;
以上都不满足,则继续往下执行;topic长度不能超过127字节,属性长度不能超过32767字节;

3.4.2)处理延迟消息

如果延迟时间大于0,则属于延迟消息;
3.4.2.1)修改topic和queueId
修改topic为SCHEDULE_TOPIC_XXXX,queueId为延迟时间对应的延迟级别;
3.4.2.2)备份原始的topic和queueId
原始的topic和queueId放进属性中,其中key分别为REAL_TOPIC,REAL_QID;

3.4.3)获取commitLog中正在写的mappedFile

3.4.3.1)计算mappedFile文件的名字
获取commitLog中mfq中集合的最后一个mappedFile文件;若为null表示mfq的list集合为空,此时需要创建第一个mappedFile文件,则执行MappedFileQueue#getLastMappedFile(0)方法,0表示下一个mappedFile文件的名字;若满了,则执行getLastMappedFile(long startOffset)方法,此时的startOffset的计算方法是最后一个mappedFile的名字加上1g,得到下一个mappedFile的名字;
3.4.3.2)创建mappedFile文件
3.4.3.2.1)putRequestAndReturnMappedFile方法
根据上一步创建的连续两个mappedFile文件名字,这里也连续创建两个AllocateRequest实例,该实例中主要封装了mappedFile的filePath和fileSize,连续创建两个目的是提高创建效率,连续两次执行requestQueue.offer方法,此时会唤醒执行requestQueue.take方法而等待的线程;随后当前线程又调用jdk的CountDownLatch#await阻塞;
3.4.3.2.2)allocateMappedFileService线程的run方法
另一边,allocateMappedFileService线程会一直循环,但执行到requestQueue.take若没有拿到任务则会一直等待,当队列中有任务时则会停止等待,继续执行,根据allocateRequest中的path和size执行MappedFile的构造方法,拿到MappedFile实例后,执行其warmMappedFile方法进行预热,MappedFile#warmMappedFile方法,目的是创建虚拟内存与物理内存之间的映射关系,如果没有提前建立好映射,在写的时候会产生缺页异常,再建立映射,会影响效率,而提前在每页都写一个0,就会提前产生缺页异常,提前建立好映射;到真正用的时候效率就比较高;
最终执行countDown方法,唤醒阻塞的线程;关于MappedFile的实例化在将commitLog的load时已讲过;
3.4.3.2.3)allocateMappedFileService为null
此时直接调用MappedFile的构造方法,但此时只支持创建一个mappedFile文件;
最终将创建的MappedFile实例加入到当前commitLog的mfq的list集合中;若mappedFile为null,则直接返回PutMessageStatus.CREATE_MAPEDFILE_FAILED给生产者;

3.4.4)MappedFile#appendMessage方法

在追加前先执行PutMessageSpinLock#lock方法,保证追加操作是串行的;接着拿到commitLog中当前正在写的MappedFile实例后,执行该实例的appendMessage方法,向该实例中追加消息,两个入参一个是需要追加的消息,一个是回调方法;
3.4.4.1)mappedFileBuffer.slice方法
获取当前mappedFile的wrotePosition,若小于当前mappedFile的大小即1个g,则执行mappedFileBuffer.slice方法获取当前mappedFile的一个buffer副本,设置buffer副本的position为当前mappedFile的wrotePosition;
3.4.4.2)DefaultAppendMessageCallback#doAppend方法
拿到mappedFile的切片后,执行doAppend方法,入参有四个,依次是当前mappedFile的名字,byteBuffer副本,byteBuffer副本剩余内存大小,以及需要追加的消息;
3.4.4.2.1)初始化topicQueueTable中的topic-queueId对应的value
该表的key为topic-queueId,value为对应的consumeQueue中消息数量;topic和queueId均可以从需要追加的消息中获取,若对应的value为null,则初始化为0,若value不为null,则不作任何处理;
3.4.4.2.2)检查属性和消息体大小是否超了和剩余空间
若属性大小超过32kb,消息体大小超过4mb,则分别返回AppendMessageStatus.PROPERTIES_SIZE_EXCEEDED,AppendMessageStatus.MESSAGE_SIZE_EXCEEDED状态封装进AppendMessageResult实例中;
3.4.4.2.3)检查剩余空间是否够用
若消息的大小加8字节大于当前byteBuffer副本剩余内存大小,则表示当前mappedFile需要结束了,所以在后边加上4字节表示剩余空间大小,再加上4字节表示结尾魔数;追加时,先放进一个ByteBuffer实例msgStoreItemMemory中,再将该实例放进byteBuffer副本中;最后封装成一个AppendMessageResult实例返回,此时返回状态为AppendMessageStatus.END_OF_FILE,还封装了当前mappedFile的写入的初始位点,当前mappedFile的剩余空间,queueOffset(topicQueueTable的value);
3.4.4.2.4)将每个字段先放入缓存msgStoreItemMemory
按照commitLog的mappedFile中每条消息的格式顺序,依次将各个字段放入msgStoreItemMemory中,这是一个大小为4mb+8byte大小的byteBuffer,表示每条消息最大为4mb,最后执行副本byteBuffer的put方法,入参为msgStoreItemMemory,然后返回AppendMessageStatus.PUT_OK;最后更新当前mappedFile的wrotePosition和storeTimestamp;

3.4.5)判断返回状态

判断完以下四种状态后,接着执行putMessageLock.unlock()方法;在执行完刷盘和ha后,最终返回putMessageResult;

3.4.5.1)返回PUT_OK
则继续执行后续的刷盘和ha操作;
3.4.5.2)返回END_OF_FILE
则新建一个mappedFile,再执行追加;
3.4.5.3)返回xxx_EXCEEDED
xxx包括MSSAGE_SIZE和PROPERTIES_SIZE,则直接返回PutMessageResult实例,封装了MESSAGE_ILLEGAL状态;
3.4.5.4)返回UNKNOWN_ERROR
则直接返回PutMessageResult实例,封装了UNKNOWN_ERROR状态;
执行完追加后,先会执行putMessageLock.unLock方法,释放自旋锁,接着会执行DefaultMessageStore#unlockMappedFile方法,入参是unlockMappedFile,保存着上一个已写满的mappedFile文件(实际上并未写满,只是剩余空间不够用),该方法会往调度线程池中提交一个任务,任务中会执行MappedFile#munlock方法,将mappedByteBuffer从lock状态切换为unlock状态;最后继续执行后边的执行刷盘,ha等操作;

3.4.6)handleDiskFlush方法

3.4.7)handleHA方法

  • 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、付费专栏及课程。

余额充值