RocketMQ源码分析之Message拉取与消费(上Broker把消息推送到对应的消费队列)

本文详细分析RocketMQ的消费逻辑,包括ConsumeQueue的存储消息方式,ReputMessageService和FlushConsumeQueueService的作用。ReputMessageService负责消息写入与索引生成,而FlushConsumeQueueService执行消息刷盘。拉取消息的接口校验了各种条件,如订阅版本号,然后通过MessageStore#getMessage()获取消息。
摘要由CSDN通过智能技术生成

消费逻辑图:

这里写图片描述
消费者队列根据Topic、QueueId分别存储相应的消息在CommitLog中的位置信息(offset、size、tagsCode),因此Consume Queue并没有存储实际要消费的信息,而是需要通过实际存储的CommitLog中的位置信息在找一遍真正的要消费的信息

消费顺序简图

这里写图片描述

ConsumeQueue存储消息

主要有两个服务组件:
- ReputMessageService:用来对消费队列执行写操作
- FlushConsumeQueueService:用来执行消费队列中的消息的刷盘服务,即将消费者队列中的内存消息刷盘到本地磁盘做持久化处理

ReputMessageService

这里写图片描述
1、只要该线程服务没有停掉,就会一直调用deReput方法生成消息位置信息到消费队列,并且会不断生成消息索引到索引文件(IndexFile)

 public void run() {
         DefaultMessageStore.log.info(this.getServiceName() + " service started");

         while (!this.isStopped()) {
             try {
                 Thread.sleep(1);
                 this.doReput();
             } catch (Exception e) {
                 DefaultMessageStore.log.warn(this.getServiceName() + " service has exception. ", e);
             }
         }

         DefaultMessageStore.log.info(this.getServiceName() + " service end");
     }

2、在这个doReput中,只要CommitLog可以重放消息(通过比较reputFromOffset < DefaultMessageStore.this.commitLog.getMaxOffset()),就会一直进行循环拉取操作,除非消息能够在多个MeesageStore中存储或者reput起始位大于所规定的的固定起始位,doReput方法会直接结束。

private void doReput() {
 52:         for (boolean doNext = true; this.isCommitLogAvailable() && doNext; ) {
 53: 
 54:             // TODO 疑问:这个是啥
 55:             if (DefaultMessageStore.this.getMessageStoreConfig().isDuplicationEnable() //
 56:                 && this.reputFromOffset >= DefaultMessageStore.this.getConfirmOffset()) {
 57:                 break;
 58:             }
 59: 
 60:             // 获取从reputFromOffset开始的commitLog对应的MappeFile对应的MappedByteBuffer
                 //这里的reputFromOffset是不断变化的,就好比你读完一条消息后,你再去读下一条消息在CommitLogd中的位置起始点会发生变化;如果这个点到达了了这个CommitLog的最大值,就会找下一个mappedFile再读,再移动这个值
 61:             SelectMappedBufferResult result = DefaultMessageStore.this.commitLog.getData(reputFromOffset);
 62:             if (result != null) {
 63:                 try {
 64:                     this.reputFromOffset = result.getStartOffset();
 65: 
 66:                     // 遍历MappedByteBuffer
 67:                     for (int readSize = 0; readSize < result.getSize() && doNext; ) {
 68:                         // 生成重放消息重放调度请求
 69:                         DispatchRequest dispatchRequest = DefaultMessageStore.this.commitLog.checkMessageAndReturnSize(result.getByteBuffer(), false, false);
 70:                         int size = dispatchRequest.getMsgSize(); // 消息长度
 71:                         // 根据请求的结果处理
 72:                         if (dispatchRequest.isSuccess()) { // 构建重放消息调度请求成功了(将buffer的消息全部读取到这个请求里面去了)
 73:                             if (size > 0) { // 读取到了Message,那么会立即执行将消息放到消费队列的调度任务
 74:                                 DefaultMessageStore.this.doDispatch(dispatchRequest);//这个调度任务里面会将非事务消息 或 事务提交消息 建立 消息位置信息 到 ConsumeQueue【putMessagePositionInfo(String topic, int queueId, long offset, int size, long tagsCode, long storeTimestamp,
long logicOffset)实现的】,并且会建立索引信息到 、IndexFile
 75:                                 // 并且如果该Broker是主节点且一直保持长连接的话,会立即通知对应的Topic、queue时间监听器将会有新消息到达,让其做好准备
 76:                                 if (BrokerRole.SLAVE != DefaultMessageStore.this.getMessageStoreConfig().getBrokerRole()
 77:                                     && DefaultMessageStore.this.brokerConfig.isLongPollingEnable()) {
 78:                                     DefaultMessageStore.this.messageArrivingListener.arriving(dispatchRequest.getTopic(),
 79:                                         dispatchRequest.getQueueId(), dispatchRequest.getConsumeQueueOffset() + 1,
 80:                                         dispatchRequest.getTagsCode());
 81:                                 }
 82:                                 // FIXED BUG By shijia
 83:                                 this.reputFromOffset += size;
 84:                                 readSize += size;
 85:                                 // 统计
 86:                                 if (DefaultMessageStore.this.getMessageStoreConfig().getBrokerRole() == BrokerRole.SLAVE) {
 87:                                     DefaultMessageStore.this.storeStatsService
 88:                                         .getSinglePutMessageTopicTimesTotal(dispatchRequest.getTopic()).incrementAndGet();
 89:                                     DefaultMessageStore.this.storeStatsService
 90:                                         .getSinglePutMessageTopicSizeTotal(dispatchRequest.getTopic())
 91:                                         .addAndGet(dispatchRequest.getMsgSize());
 92:                                 }
 93:                             } else if (size == 0
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值