RocketMQ源码分析之Broker处理拉取消息请求(2)

本文深入探讨RocketMQ Broker如何处理拉取消息请求,涉及订阅tag匹配、CommitLog读取、消息过滤、下次拉取下标计算以及不同状态判断。分析了在消息不足时如何设置下次拉取,并关注性能优化,如避免Heap操作以提升拉取效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

判断是否有额外信息,暂不考虑。

public boolean isExtAddr(long tagsCode) {
    return ConsumeQueueExt.isExtAddr(tagsCode);
}

public static boolean isExtAddr(final long address) {
    return address <= MAX_ADDR;
}

判断订阅的tag哈希值是否包含该消息的tag哈希值。

public boolean isMatchedByConsumeQueue(Long tagsCode, ConsumeQueueExt.CqExtUnit cqExtUnit) {
    if (null == subscriptionData) {
        return true;
    }

    if (subscriptionData.isClassFilterMode()) {
        return true;
    }

    // by tags code.
    if (ExpressionType.isTagType(subscriptionData.getExpressionType())) {

        if (tagsCode == null) {
            return true;
        }

        if (subscriptionData.getSubString().equals(SubscriptionData.SUB_ALL)) {
            return true;
        }

        return subscriptionData.getCodeSet().contains(tagsCode.intValue());
    } else {
        // no expression or no bloom
        if (consumerFilterData == null || consumerFilterData.getExpression() == null
            || consumerFilterData.getCompiledExpression() == null || consumerFilterData.getBloomFilterData() == null) {
            return true;
        }

        // message is before consumer
        if (cqExtUnit == null || !consumerFilterData.isMsgInLive(cqExtUnit.getMsgStoreTime())) {
            log.debug("Pull matched because not in live: {}, {}", consumerFilterData, cqExtUnit);
            return true;
        }

        byte[] filterBitMap = cqExtUnit.getFilterBitMap();
        BloomFilter bloomFilter = this.consumerFilterManager.getBloomFilter();
        if (filterBitMap == null || !this.bloomDataValid
            || filterBitMap.length * Byte.SIZE != consumerFilterData.getBloomFilterData().getBitNum()) {
            return true;
        }

        BitsArray bitsArray = null;
        try {
            bitsArray = BitsArray.create(filterBitMap);
            boolean ret = bloomFilter.isHit(consumerFilterData.getBloomFilterData(), bitsArray);
            log.debug("Pull {} by bit map:{}, {}, {}", ret, consumerFilterData, bitsArray, cqExtUnit);
            return ret;
        } catch (Throwable e) {
            log.error("bloom filter error, sub=" + subscriptionData
                + ", filter=" + consumerFilterData + ", bitMap=" + bitsArray, e);
        }
    }

    return true;
}

从commitLog中获取消息

public SelectMappedBufferResult getMessage(final long offset, final int size) {
    int mappedFileSize = this.defaultMessageStore.getMessageStoreConfig().getMapedFileSizeCommitLog();
    MappedFile mappedFile = this.mappedFileQueue.findMappedFileByOffset(offset, offset == 0);
    if (mappedFile != null) {
        int pos = (int) (offset % mappedFileSize);
        return mappedFile.selectMappedBuffer(pos, size);
    }
    return null;
}

对消息进行再过滤

public boolean isMatchedByCommitLog(ByteBuffer msgBuffer, Map<String, String> properties) {
    if (subscriptionData == null) {
        return true;
    }

    if (subscriptionData.isClassFilterMode()) {
        return true;
    }

    if (ExpressionType.isTagType(subscriptionData.getExpressionType())) {
        return true;
    }

    ConsumerFilterData realFilterData = this.consumerFilterData;
    Map<String, String> tempProperties = properties;

    // no expression
    if (realFilterData == null || realFilterData.getExpression() == null
        || realFilterData.getCompiledExpression() == null) {
        return true;
    }

    if (tempProperties == null && msgBuffer != null) {
        tempProperties = MessageDecoder.decodeProperties(msgBuffer);
    }

    Object ret = null;
    try {
        MessageEvaluationContext context = new MessageEvaluationContext(tempProperties);

        ret = realFilterData.getCompiledExpression().evaluate(context);
    } catch (Throwable e) {
        log.error("Message Filter error, " + realFilterData + ", " + tempProperties, e);
    }

    log.debug("Pull eval result: {}, {}, {}", ret, realFilterData, tempProperties);

    if (ret == null || !(ret instanceof Boolean)) {
        return false;
    }

    return (Boolean) ret;
}

最后把满足要求的消息放入结果中


public void addMessage(final SelectMappedBufferResult mapedBuffer) {
    this.messageMapedList.add(mapedBuffer);
    this.messageBufferList.add(mapedBuffer.getByteBuffer());
    this.bufferTotalSize += mapedBuffer.getSize();
    this.msgCount4Commercial += (int) Math.ceil(
        mapedBuffer.getSize() / BrokerStatsManager.SIZE_PER_COUNT);
}

计算下一次拉取的下标,判断当前获取的最后一个消息与最大消息偏移量之前的间隔是否超过限制,超过的话设置下一次从从broker拉取消息。默认从brokerId为whichBrokerWhenConsumeSlowly = 1。

response.setRemark(getMessageResult.getStatus().name());
responseHeader.setNextBeginOffset(getMessageResult.getNextBeginOffset());
responseHeader.setMinOffset(getMessageResult.getMinOffset());
responseHeader.setMaxOffset(getMessageResult.getMaxOffset());

if (getMessageResult.isSuggestPullingFromSlave()) {
    responseHeader.setSuggestWhichBrokerId(subscriptionGroupConfig.getWhichBrokerWhenConsumeSlowly());
} else {
    responseHeader.setSuggestWhichBrokerId(MixAll.MASTER_ID);
}

switch (this.brokerController.getMessageStoreConfig().getBrokerRole()) {
    case ASYNC_MASTER:
    case SYNC_MASTER:
        break;
    case SLAVE:
        if (!this.brokerController.getBrokerConfig().isSlaveReadEnable()) {
            response.setCode(ResponseCode.PULL_RETRY_IMMEDIATELY);
            responseHeader.setSuggestWhichBrokerId(MixAll.MASTER_ID);
        }
        break;
}

if (this.brokerController.getBrokerConfig().isSlaveReadEnable()) {
    // consume too slow ,redirect to another machine
    if (getMessageResult.isSuggestPullingFromSlave()) {
        responseHeader.setSuggestWhichBrokerId(subscriptionGroupConfig.getWhichBrokerWhenConsumeSlowly());
    }
    // consume ok
    else {
        responseHeader.setSuggestWhichBrokerId(subscriptionGroupConfig.getBrokerId());
    }
} else {
    responseHeader.setSuggestWhichBrokerId(MixAll.MASTER_ID);
}

判断不同的状态,状态FOUND代表获取到消息,OFFSET_OVERFLOW_ONE代表目前没有新消息,需要下一次拉取,设置状态为PULL_NOT_FOUND。

switch (getMessageResult.getStatus()) {
case FOUND:
    response.setCode(ResponseCode.SUCCESS);
    break;
case MESSAGE_WAS_REMOVING:
    response.setCode(ResponseCode.PULL_RETRY_IMMEDIATELY);
    break;
case NO_MATCHED_LOGIC_QUEUE:
case NO_MESSAGE_IN_QUEUE:
    if (0 != requestHeader.getQueueOffset()) {
        response.setCode(ResponseCode.PULL_OFFSET_MOVED);

        // XXX: warn and notify me
        log.info("the broker store no queue data, fix the request offset {} to {}, Topic: {} QueueId: {} Consumer Group: {}",
            requestHeader.getQueueOffset(),
            getMessageResult.getNextBeginOffset(),
            requestHeader.getTopic(),
            requestHeader.getQueueId(),
            requestHeader.getConsumerGroup()
        );
    } else {
        response.setCode(ResponseCode.PULL_NOT_FOUND);
    }
    break;
case NO_MATCHED_MESSAGE:
    response.setCode(ResponseCode.PULL_RETRY_IMMEDIATELY);
    break;
case OFFSET_FOUND_NULL:
    response.setCode(ResponseCode.PULL_NOT_FOUND);
    break;
case OFFSET_OVERFLOW_BADLY:
    response.setCode(ResponseCode.PULL_OFFSET_MOVED);
    // XXX: warn and notify me
    log.info("the request offset: {} over flow badly, broker max offset: {}, consumer: {}",
        requestHeader.getQueueOffset(), getMessageResult.getMaxOffset(), channel.remoteAddress());
    break;
case OFFSET_OVERFLOW_ONE:
    response.setCode(ResponseCode.PULL_NOT_FOUND);
    break;
case OFFSET_TOO_SMALL:
    response.setCode(ResponseCode.PULL_OFFSET_MOVED);
    log.info("the request offset too small. group={}, topic={}, requestOffset={}, brokerMinOffset={}, clientIp={}",
        requestHeader.getConsumerGroup(), requestHeader.getTopic(), requestHeader.getQueueOffset(),
        getMessageResult.getMinOffset(), channel.remoteAddress());
    break;
default:
    assert false;
    break;
}

当成功获取到消息时,记录一些统计数据


case ResponseCode.SUCCESS:

this.brokerController.getBrokerStatsManager().incGroupGetNums(requestHeader.getConsumerGroup(), requestHeader.getTopic(),
    getMessageResult.getMessageCount());

this.brokerController.getBrokerStatsManager().incGroupGetSize(requestHeader.getConsumerGroup(), requestHeader.getTopic(),
    getMessageResult.getBufferTotalSize());

this.brokerController.getBrokerStatsManager().incBrokerGetNums(getMessageResult.getMessageCount());
if (this.brokerController.getBrokerConfig().isTransferMsgByHeap()) {
    final long beginTimeMills = this.brokerController.getMessageStore().now();
    final byte[] r = this.readGetMessageResult(getMessageResult, requestHeader.getConsumerGroup(), requestHeader.getTopic(), requestHeader.getQueueId());
    this.brokerController.getBrokerStatsManager().incGroupGetLatency(requestHeader.getConsumerGroup(),
        requestHeader.getTopic(), requestHeader.getQueueId(),
        (int) (this.brokerController.getMessageStore().now() - beginTimeMills));
    response.setBody(r);
} else {
    try {
        FileRegion fileRegion =
            new ManyMessageTransfer(response.encodeHeader(getMessageResult.getBufferTotalSize()), getMessageResult);
        channel.writeAndFlush(fileRegion).addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                getMessageResult.release();
                if (!future.isSuccess()) {
                    log.error("transfer many message by pagecache failed, {}", channel.remoteAddress(), future.cause());
                }
            }
        });
    } catch (Throwable e) {
        log.error("transfer many message by pagecache exception", e);
        getMessageResult.release();
    }

    response = null;
}
break;

判断是否要从堆中获取这些消息,需要往响应response写入body。

private boolean transferMsgByHeap = true

private byte[] readGetMessageResult(final GetMessageResult getMessageResult, final String group, final String topic,
    final int queueId) {
    final ByteBuffer byteBuffer = ByteBuffer.allocate(getMessageResult.getBufferTotalSize());

    long storeTimestamp = 0;
    try {
        List<ByteBuffer> messageBufferList = getMessageResult.getMessageBufferList();
        for (ByteBuffer bb : messageBufferList) {

            byteBuffer.put(bb);
            storeTimestamp = bb.getLong(MessageDecoder.MESSAGE_STORE_TIMESTAMP_POSTION);
        }
    } finally {
        getMessageResult.release();
    }

    this.brokerController.getBrokerStatsManager().recordDiskFallBehindTime(group, topic, queueId, this.brokerController.getMessageStore().now() - storeTimestamp);
    return byteBuffer.array();
}

从文件中直接映射获取,官方建议关闭transferMsgByHeap,提高拉消息效率,因为他已经通过channel.writeAndFlush回复消费者了,不需要往响应response写入body,也不需要response,把它置为空,防止重复回应。


public long position() {
    int pos = byteBufferHeader.position();
    List<ByteBuffer> messageBufferList = this.getMessageResult.getMessageBufferList();
    for (ByteBuffer bb : messageBufferList) {
        pos += bb.position();
    }
    return pos;
}

public long transferTo(WritableByteChannel target, long position) throws IOException {
  if (this.byteBufferHeader.hasRemaining()) {
      transferred += target.write(this.byteBufferHeader);
      return transferred;
  } else {
      List<ByteBuffer> messageBufferList = this.getMessageResult.getMessageBufferList();
      for (ByteBuffer bb : messageBufferList) {
          if (bb.hasRemaining()) {
              transferred += target.write(bb);
              return transferred;
          }
      }
  }

  return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值