【入门到放弃】RocketMQ如何处理消费请求

516 阅读7分钟

省流

文章从源码走了一遍brokerController如何处理一个rocketmq消费者发送上来的pullMessageRequest请求,可以根据侧边目录快速定位需要了解的内容

处理一个请求的过程是

  • broker的server接收到pullMessageReuest
  • 调用PullMessageProcessor处理request
  • 通过messageStore.getMessageAsync从store中获取消息
  • pullMessageResultHandler处理消息后返回

PullMessageProcessor

构建

在构建BrokerController的时候,new了PullMessageProcessor对象并配置给Controller的pullMessageProcessor属性

image.png

PullMessageProcessor

PullMessageProcessor实现了NettyRequestProcessor,主要的业务处理是processRequest方法

public class PullMessageProcessor implements NettyRequestProcessor {
    private static final Logger LOGGER = LoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
    private List<ConsumeMessageHook> consumeMessageHookList;
    private PullMessageResultHandler pullMessageResultHandler;
    private final BrokerController brokerController;

    public PullMessageProcessor(final BrokerController brokerController) {
        this.brokerController = brokerController;
        this.pullMessageResultHandler = new DefaultPullMessageResultHandler(brokerController);
    }

processRequest 处理请求

@Override
public RemotingCommand processRequest(final ChannelHandlerContext ctx,
    RemotingCommand request) throws RemotingCommandException {
    return this.processRequest(ctx.channel(), request, true, true);
}

private RemotingCommand processRequest(final Channel channel, RemotingCommand request, boolean brokerAllowSuspend, boolean brokerAllowFlowCtrSuspend)
    throws RemotingCommandException {
    //获取store当前的时间记录为处理请求的起始时间
    final long beginTimeMills = this.brokerController.getMessageStore().now();
    //构建返回体
    RemotingCommand response = RemotingCommand.createResponseCommand(PullMessageResponseHeader.class);
    final PullMessageResponseHeader responseHeader = (PullMessageResponseHeader) response.readCustomHeader();
    final PullMessageRequestHeader requestHeader =
        (PullMessageRequestHeader) request.decodeCommandCustomHeader(PullMessageRequestHeader.class);

    response.setOpaque(request.getOpaque());
    LOGGER.debug("receive PullMessage request command, {}", request);

    //校验broker权限
    if (!PermName.isReadable(this.brokerController.getBrokerConfig().getBrokerPermission())) {
        response.setCode(ResponseCode.NO_PERMISSION);
        responseHeader.setForbiddenType(ForbiddenType.BROKER_FORBIDDEN);
        response.setRemark(String.format("the broker[%s] pulling message is forbidden",
            this.brokerController.getBrokerConfig().getBrokerIP1()));
        return response;
    }

    //如果是LitePull 但是这个broker不支持就拒绝请求
    if (request.getCode() == RequestCode.LITE_PULL_MESSAGE && !this.brokerController.getBrokerConfig().isLitePullMessageEnable()) {
        response.setCode(ResponseCode.NO_PERMISSION);
        responseHeader.setForbiddenType(ForbiddenType.BROKER_FORBIDDEN);
        response.setRemark(
            "the broker[" + this.brokerController.getBrokerConfig().getBrokerIP1() + "] for lite pull consumer is forbidden");
        return response;
    }

    //获取消费者组的订阅信息
    SubscriptionGroupConfig subscriptionGroupConfig =
        this.brokerController.getSubscriptionGroupManager().findSubscriptionGroupConfig(requestHeader.getConsumerGroup());
    if (null == subscriptionGroupConfig) {
        response.setCode(ResponseCode.SUBSCRIPTION_GROUP_NOT_EXIST);
        response.setRemark(String.format("subscription group [%s] does not exist, %s", requestHeader.getConsumerGroup(), FAQUrl.suggestTodo(FAQUrl.SUBSCRIPTION_GROUP_NOT_EXIST)));
        return response;
    }

    //校验订阅信息是否允许消费
    if (!subscriptionGroupConfig.isConsumeEnable()) {
        response.setCode(ResponseCode.NO_PERMISSION);
        responseHeader.setForbiddenType(ForbiddenType.GROUP_FORBIDDEN);
        response.setRemark("subscription group no permission, " + requestHeader.getConsumerGroup());
        return response;
    }

    //校验topic
    TopicConfig topicConfig = this.brokerController.getTopicConfigManager().selectTopicConfig(requestHeader.getTopic());
    if (null == topicConfig) {
        LOGGER.error("the topic {} not exist, consumer: {}", requestHeader.getTopic(), RemotingHelper.parseChannelRemoteAddr(channel));
        response.setCode(ResponseCode.TOPIC_NOT_EXIST);
        response.setRemark(String.format("topic[%s] not exist, apply first please! %s", requestHeader.getTopic(), FAQUrl.suggestTodo(FAQUrl.APPLY_TOPIC_URL)));
        return response;
    }

    //校验topic权限
    if (!PermName.isReadable(topicConfig.getPerm())) {
        response.setCode(ResponseCode.NO_PERMISSION);
        responseHeader.setForbiddenType(ForbiddenType.TOPIC_FORBIDDEN);
        response.setRemark("the topic[" + requestHeader.getTopic() + "] pulling message is forbidden");
        return response;
    }

    //构建topic的队列上下文
    TopicQueueMappingContext mappingContext = this.brokerController.getTopicQueueMappingManager().buildTopicQueueMappingContext(requestHeader, false);
    {
        RemotingCommand rewriteResult = rewriteRequestForStaticTopic(requestHeader, mappingContext);
        if (rewriteResult != null) {
            return rewriteResult;
        }
    }

    //校验queueId是否合法
    if (requestHeader.getQueueId() < 0 || requestHeader.getQueueId() >= topicConfig.getReadQueueNums()) {
        String errorInfo = String.format("queueId[%d] is illegal, topic:[%s] topicConfig.readQueueNums:[%d] consumer:[%s]",
            requestHeader.getQueueId(), requestHeader.getTopic(), topicConfig.getReadQueueNums(), channel.remoteAddress());
        LOGGER.warn(errorInfo);
        response.setCode(ResponseCode.SYSTEM_ERROR);
        response.setRemark(errorInfo);
        return response;
    }

    //获取消费者管理器,并补充消费者信息
    ConsumerManager consumerManager = brokerController.getConsumerManager();
    switch (RequestSource.parseInteger(requestHeader.getRequestSource())) {
        case PROXY_FOR_BROADCAST:
            consumerManager.compensateBasicConsumerInfo(requestHeader.getConsumerGroup(), ConsumeType.CONSUME_PASSIVELY, MessageModel.BROADCASTING);
            break;
        case PROXY_FOR_STREAM:
            consumerManager.compensateBasicConsumerInfo(requestHeader.getConsumerGroup(), ConsumeType.CONSUME_ACTIVELY, MessageModel.CLUSTERING);
            break;
        default:
            consumerManager.compensateBasicConsumerInfo(requestHeader.getConsumerGroup(), ConsumeType.CONSUME_PASSIVELY, MessageModel.CLUSTERING);
            break;
    }

    //校验订阅数据
    SubscriptionData subscriptionData = null;
    ConsumerFilterData consumerFilterData = null;
    final boolean hasSubscriptionFlag = PullSysFlag.hasSubscriptionFlag(requestHeader.getSysFlag());
    if (hasSubscriptionFlag) {
        try {
            subscriptionData = FilterAPI.build(
                requestHeader.getTopic(), requestHeader.getSubscription(), requestHeader.getExpressionType()
            );
            consumerManager.compensateSubscribeData(requestHeader.getConsumerGroup(), requestHeader.getTopic(), subscriptionData);

            if (!ExpressionType.isTagType(subscriptionData.getExpressionType())) {
                consumerFilterData = ConsumerFilterManager.build(
                    requestHeader.getTopic(), requestHeader.getConsumerGroup(), requestHeader.getSubscription(),
                    requestHeader.getExpressionType(), requestHeader.getSubVersion()
                );
                assert consumerFilterData != null;
            }
        } catch (Exception e) {
            LOGGER.warn("Parse the consumer's subscription[{}] failed, group: {}", requestHeader.getSubscription(),
                requestHeader.getConsumerGroup());
            response.setCode(ResponseCode.SUBSCRIPTION_PARSE_FAILED);
            response.setRemark("parse the consumer's subscription failed");
            return response;
        }
    } else {
        ConsumerGroupInfo consumerGroupInfo =
            this.brokerController.getConsumerManager().getConsumerGroupInfo(requestHeader.getConsumerGroup());
        if (null == consumerGroupInfo) {
            LOGGER.warn("the consumer's group info not exist, group: {}", requestHeader.getConsumerGroup());
            response.setCode(ResponseCode.SUBSCRIPTION_NOT_EXIST);
            response.setRemark("the consumer's group info not exist" + FAQUrl.suggestTodo(FAQUrl.SAME_GROUP_DIFFERENT_TOPIC));
            return response;
        }

        if (!subscriptionGroupConfig.isConsumeBroadcastEnable()
            && consumerGroupInfo.getMessageModel() == MessageModel.BROADCASTING) {
            response.setCode(ResponseCode.NO_PERMISSION);
            responseHeader.setForbiddenType(ForbiddenType.BROADCASTING_DISABLE_FORBIDDEN);
            response.setRemark("the consumer group[" + requestHeader.getConsumerGroup() + "] can not consume by broadcast way");
            return response;
        }

        boolean readForbidden = this.brokerController.getSubscriptionGroupManager().getForbidden(//
            subscriptionGroupConfig.getGroupName(), requestHeader.getTopic(), PermName.INDEX_PERM_READ);
        if (readForbidden) {
            response.setCode(ResponseCode.NO_PERMISSION);
            responseHeader.setForbiddenType(ForbiddenType.SUBSCRIPTION_FORBIDDEN);
            response.setRemark("the consumer group[" + requestHeader.getConsumerGroup() + "] is forbidden for topic[" + requestHeader.getTopic() + "]");
            return response;
        }

        subscriptionData = consumerGroupInfo.findSubscriptionData(requestHeader.getTopic());
        if (null == subscriptionData) {
            LOGGER.warn("the consumer's subscription not exist, group: {}, topic:{}", requestHeader.getConsumerGroup(), requestHeader.getTopic());
            response.setCode(ResponseCode.SUBSCRIPTION_NOT_EXIST);
            response.setRemark("the consumer's subscription not exist" + FAQUrl.suggestTodo(FAQUrl.SAME_GROUP_DIFFERENT_TOPIC));
            return response;
        }

        if (subscriptionData.getSubVersion() < requestHeader.getSubVersion()) {
            LOGGER.warn("The broker's subscription is not latest, group: {} {}", requestHeader.getConsumerGroup(),
                subscriptionData.getSubString());
            response.setCode(ResponseCode.SUBSCRIPTION_NOT_LATEST);
            response.setRemark("the consumer's subscription not latest");
            return response;
        }
        if (!ExpressionType.isTagType(subscriptionData.getExpressionType())) {
            consumerFilterData = this.brokerController.getConsumerFilterManager().get(requestHeader.getTopic(),
                requestHeader.getConsumerGroup());
            if (consumerFilterData == null) {
                response.setCode(ResponseCode.FILTER_DATA_NOT_EXIST);
                response.setRemark("The broker's consumer filter data is not exist!Your expression may be wrong!");
                return response;
            }
            if (consumerFilterData.getClientVersion() < requestHeader.getSubVersion()) {
                LOGGER.warn("The broker's consumer filter data is not latest, group: {}, topic: {}, serverV: {}, clientV: {}",
                    requestHeader.getConsumerGroup(), requestHeader.getTopic(), consumerFilterData.getClientVersion(), requestHeader.getSubVersion());
                response.setCode(ResponseCode.FILTER_DATA_NOT_LATEST);
                response.setRemark("the consumer's consumer filter data not latest");
                return response;
            }
        }
    }

    //校验tag是否支持
    if (!ExpressionType.isTagType(subscriptionData.getExpressionType())
        && !this.brokerController.getBrokerConfig().isEnablePropertyFilter()) {
        response.setCode(ResponseCode.SYSTEM_ERROR);
        response.setRemark("The broker does not support consumer to filter message by " + subscriptionData.getExpressionType());
        return response;
    }

    //获取消息过滤器
    MessageFilter messageFilter;
    if (this.brokerController.getBrokerConfig().isFilterSupportRetry()) {
        messageFilter = new ExpressionForRetryMessageFilter(subscriptionData, consumerFilterData,
            this.brokerController.getConsumerFilterManager());
    } else {
        messageFilter = new ExpressionMessageFilter(subscriptionData, consumerFilterData,
            this.brokerController.getConsumerFilterManager());
    }

    //获取  broker的store
    final MessageStore messageStore = brokerController.getMessageStore();
    if (this.brokerController.getMessageStore() instanceof DefaultMessageStore) {
        DefaultMessageStore defaultMessageStore = (DefaultMessageStore)this.brokerController.getMessageStore();
        boolean cgNeedColdDataFlowCtr = brokerController.getColdDataCgCtrService().isCgNeedColdDataFlowCtr(requestHeader.getConsumerGroup());
        if (cgNeedColdDataFlowCtr) {
            boolean isMsgLogicCold = defaultMessageStore.getCommitLog()
                .getColdDataCheckService().isMsgInColdArea(requestHeader.getConsumerGroup(),
                    requestHeader.getTopic(), requestHeader.getQueueId(), requestHeader.getQueueOffset());
            if (isMsgLogicCold) {
                ConsumeType consumeType = this.brokerController.getConsumerManager().getConsumerGroupInfo(requestHeader.getConsumerGroup()).getConsumeType();
                if (consumeType == ConsumeType.CONSUME_PASSIVELY) {
                    response.setCode(ResponseCode.SYSTEM_BUSY);
                    response.setRemark("This consumer group is reading cold data. It has been flow control");
                    return response;
                } else if (consumeType == ConsumeType.CONSUME_ACTIVELY) {
                    if (brokerAllowFlowCtrSuspend) {  // second arrived, which will not be held
                        PullRequest pullRequest = new PullRequest(request, channel, 1000,
                            this.brokerController.getMessageStore().now(), requestHeader.getQueueOffset(), subscriptionData, messageFilter);
                        this.brokerController.getColdDataPullRequestHoldService().suspendColdDataReadRequest(pullRequest);
                        return null;
                    }
                    requestHeader.setMaxMsgNums(1);
                }
            }
        }
    }


    //计算消息offset
    final boolean useResetOffsetFeature = brokerController.getBrokerConfig().isUseServerSideResetOffset();
    String topic = requestHeader.getTopic();
    String group = requestHeader.getConsumerGroup();
    int queueId = requestHeader.getQueueId();
    Long resetOffset = brokerController.getConsumerOffsetManager().queryThenEraseResetOffset(topic, group, queueId);

    GetMessageResult getMessageResult = null;
    //如果复位offset,并且获取到的offset不为空则构建状态为OFFSET_RESET的GetMessageResult,然后调用pullMessageResultHandler.handle后返回
    if (useResetOffsetFeature && null != resetOffset) {
        getMessageResult = new GetMessageResult();
        getMessageResult.setStatus(GetMessageStatus.OFFSET_RESET);
        getMessageResult.setNextBeginOffset(resetOffset);
        getMessageResult.setMinOffset(messageStore.getMinOffsetInQueue(topic, queueId));
        getMessageResult.setMaxOffset(messageStore.getMaxOffsetInQueue(topic, queueId));
        getMessageResult.setSuggestPullingFromSlave(false);
    } else {
        //获取offset便宜,如果便宜>0  返回OFFSET_RESET的GetMessageResult
        long broadcastInitOffset = queryBroadcastPullInitOffset(topic, group, queueId, requestHeader, channel);
        if (broadcastInitOffset >= 0) {
            getMessageResult = new GetMessageResult();
            getMessageResult.setStatus(GetMessageStatus.OFFSET_RESET);
            getMessageResult.setNextBeginOffset(broadcastInitOffset);
        } else {
            //如果broadcastInitOffset <0  即 -1时,调用store获取message
            SubscriptionData finalSubscriptionData = subscriptionData;
            RemotingCommand finalResponse = response;
            messageStore.getMessageAsync(group, topic, queueId, requestHeader.getQueueOffset(),
                    requestHeader.getMaxMsgNums(), messageFilter)
                .thenApply(result -> {
                    if (null == result) {
                        finalResponse.setCode(ResponseCode.SYSTEM_ERROR);
                        finalResponse.setRemark("store getMessage return null");
                        return finalResponse;
                    }
                    brokerController.getColdDataCgCtrService().coldAcc(requestHeader.getConsumerGroup(), result.getColdDataSum());
                    //handler 消息
                    return pullMessageResultHandler.handle(
                        result,
                        request,
                        requestHeader,
                        channel,
                        finalSubscriptionData,
                        subscriptionGroupConfig,
                        brokerAllowSuspend,
                        messageFilter,
                        finalResponse,
                        mappingContext,
                        beginTimeMills
                    );
                })
                .thenAccept(result -> NettyRemotingAbstract.writeResponse(channel, request, result));
        }
    }

    if (getMessageResult != null) {

        return this.pullMessageResultHandler.handle(
            getMessageResult,
            request,
            requestHeader,
            channel,
            subscriptionData,
            subscriptionGroupConfig,
            brokerAllowSuspend,
            messageFilter,
            response,
            mappingContext,
            beginTimeMills
        );
    }
    return null;
}

processRequest对请求进行验证校验请求是否合法,请求校验通过后校验offset是否正常,否则返回对齐offset的消息,如果offset正常的话,调用消息仓库获取消息

getMessageAsync获取消息

来看看如何获取一条消息,来看看DefaultMessageStore的实现

@Override
public GetMessageResult getMessage(final String group, final String topic, final int queueId, final long offset,
    final int maxMsgNums, final MessageFilter messageFilter) {
    return getMessage(group, topic, queueId, offset, maxMsgNums, MAX_PULL_MSG_SIZE, messageFilter);
}

@Override
public CompletableFuture<GetMessageResult> getMessageAsync(String group, String topic,
    int queueId, long offset, int maxMsgNums, MessageFilter messageFilter) {
    return CompletableFuture.completedFuture(getMessage(group, topic, queueId, offset, maxMsgNums, messageFilter));
}

这里是构建一个CompletableFuture去调用getMessage,来看看具体做了什么

@Override
public GetMessageResult getMessage(final String group, final String topic, final int queueId, final long offset,
    final int maxMsgNums, final int maxTotalMsgSize, final MessageFilter messageFilter) {
    //判断store是否开着
    if (this.shutdown) {
        LOGGER.warn("message store has shutdown, so getMessage is forbidden");
        return null;
    }
    //判断工作状态
    if (!this.runningFlags.isReadable()) {
        LOGGER.warn("message store is not readable, so getMessage is forbidden " + this.runningFlags.getFlagBits());
        return null;
    }
    //获取topic配置
    Optional<TopicConfig> topicConfig = getTopicConfig(topic);
    CleanupPolicy policy = CleanupPolicyUtils.getDeletePolicy(topicConfig);
    //校验topic flag是否支持key过期,是的话调用CompactionStore#getMessage,这里不深入
    if (Objects.equals(policy, CleanupPolicy.COMPACTION) && messageStoreConfig.isEnableCompaction()) {
        return compactionStore.getMessage(group, topic, queueId, offset, maxMsgNums, maxTotalMsgSize);
    } // else skip

    long beginTime = this.getSystemClock().now();

    GetMessageStatus status = GetMessageStatus.NO_MESSAGE_IN_QUEUE;
    long nextBeginOffset = offset;
    long minOffset = 0;
    long maxOffset = 0;

    //构建Result对象
    GetMessageResult getResult = new GetMessageResult();
    final long maxOffsetPy = this.commitLog.getMaxOffset();
    //获取topic对应的消费队列
    ConsumeQueueInterface consumeQueue = findConsumeQueue(topic, queueId);
    if (consumeQueue != null) {
        minOffset = consumeQueue.getMinOffsetInQueue();
        maxOffset = consumeQueue.getMaxOffsetInQueue();
        //校验offset
        if (maxOffset == 0) {
            status = GetMessageStatus.NO_MESSAGE_IN_QUEUE;
            nextBeginOffset = nextOffsetCorrection(offset, 0);
        } else if (offset < minOffset) {
            status = GetMessageStatus.OFFSET_TOO_SMALL;
            nextBeginOffset = nextOffsetCorrection(offset, minOffset);
        } else if (offset == maxOffset) {
            status = GetMessageStatus.OFFSET_OVERFLOW_ONE;
            nextBeginOffset = nextOffsetCorrection(offset, offset);
        } else if (offset > maxOffset) {
            status = GetMessageStatus.OFFSET_OVERFLOW_BADLY;
            nextBeginOffset = nextOffsetCorrection(offset, maxOffset);
        } else {
            final int maxFilterMessageSize = Math.max(this.messageStoreConfig.getMaxFilterMessageSize(), maxMsgNums * consumeQueue.getUnitSize());
            final boolean diskFallRecorded = this.messageStoreConfig.isDiskFallRecorded();

            //校验最大pull大小
            long maxPullSize = Math.max(maxTotalMsgSize, 100);
            if (maxPullSize > MAX_PULL_MSG_SIZE) {
                LOGGER.warn("The max pull size is too large maxPullSize={} topic={} queueId={}", maxPullSize, topic, queueId);
                maxPullSize = MAX_PULL_MSG_SIZE;
            }
            status = GetMessageStatus.NO_MATCHED_MESSAGE;
            long maxPhyOffsetPulling = 0;
            int cqFileNum = 0;

            while (getResult.getBufferTotalSize() <= 0
                && nextBeginOffset < maxOffset
                && cqFileNum++ < this.messageStoreConfig.getTravelCqFileNumWhenGetMessage()) {
                ReferredIterator<CqUnit> bufferConsumeQueue = null;

                try {
                    //从log文件获取buffer
                    bufferConsumeQueue = consumeQueue.iterateFrom(nextBeginOffset, maxMsgNums);

                    if (bufferConsumeQueue == null) {
                        status = GetMessageStatus.OFFSET_FOUND_NULL;
                        nextBeginOffset = nextOffsetCorrection(nextBeginOffset, this.consumeQueueStore.rollNextFile(consumeQueue, nextBeginOffset));
                        LOGGER.warn("consumer request topic: " + topic + ", offset: " + offset + ", minOffset: " + minOffset + ", maxOffset: "
                            + maxOffset + ", but access logic queue failed. Correct nextBeginOffset to " + nextBeginOffset);
                        break;
                    }

                    long nextPhyFileStartOffset = Long.MIN_VALUE;
                    //循环从buffer取出要取出的数据
                    while (bufferConsumeQueue.hasNext()
                        && nextBeginOffset < maxOffset) {
                        CqUnit cqUnit = bufferConsumeQueue.next();
                        long offsetPy = cqUnit.getPos();
                        int sizePy = cqUnit.getSize();

                        boolean isInMem = estimateInMemByCommitOffset(offsetPy, maxOffsetPy);

                        if ((cqUnit.getQueueOffset() - offset) * consumeQueue.getUnitSize() > maxFilterMessageSize) {
                            break;
                        }

                        if (this.isTheBatchFull(sizePy, cqUnit.getBatchNum(), maxMsgNums, maxPullSize, getResult.getBufferTotalSize(), getResult.getMessageCount(), isInMem)) {
                            break;
                        }

                        if (getResult.getBufferTotalSize() >= maxPullSize) {
                            break;
                        }

                        maxPhyOffsetPulling = offsetPy;

                        //Be careful, here should before the isTheBatchFull
                        nextBeginOffset = cqUnit.getQueueOffset() + cqUnit.getBatchNum();

                        if (nextPhyFileStartOffset != Long.MIN_VALUE) {
                            if (offsetPy < nextPhyFileStartOffset) {
                                continue;
                            }
                        }

                        if (messageFilter != null
                            && !messageFilter.isMatchedByConsumeQueue(cqUnit.getValidTagsCodeAsLong(), cqUnit.getCqExtUnit())) {
                            if (getResult.getBufferTotalSize() == 0) {
                                status = GetMessageStatus.NO_MATCHED_MESSAGE;
                            }

                            continue;
                        }

                        //从commitLog中取出消息
                        SelectMappedBufferResult selectResult = this.commitLog.getMessage(offsetPy, sizePy);
                        if (null == selectResult) {
                            if (getResult.getBufferTotalSize() == 0) {
                                status = GetMessageStatus.MESSAGE_WAS_REMOVING;
                            }

                            nextPhyFileStartOffset = this.commitLog.rollNextFile(offsetPy);
                            continue;
                        }

                        if (messageStoreConfig.isColdDataFlowControlEnable() && !MixAll.isSysConsumerGroupForNoColdReadLimit(group) && !selectResult.isInCache()) {
                            getResult.setColdDataSum(getResult.getColdDataSum() + sizePy);
                        }
                        //校验消息
                        if (messageFilter != null
                            && !messageFilter.isMatchedByCommitLog(selectResult.getByteBuffer().slice(), null)) {
                            if (getResult.getBufferTotalSize() == 0) {
                                status = GetMessageStatus.NO_MATCHED_MESSAGE;
                            }
                            // release...
                            selectResult.release();
                            continue;
                        }
                        //MessageTransferred计数
                        this.storeStatsService.getGetMessageTransferredMsgCount().add(cqUnit.getBatchNum());
                        //把消息写进GetMessageResult对象里
                        getResult.addMessage(selectResult, cqUnit.getQueueOffset(), cqUnit.getBatchNum());
                        //修改状态为  FOUND
                        status = GetMessageStatus.FOUND;
                        //重置nextPhyFileStartOffset
                        nextPhyFileStartOffset = Long.MIN_VALUE;
                    }
                } catch (RocksDBException e) {
                    ERROR_LOG.error("getMessage Failed. cid: {}, topic: {}, queueId: {}, offset: {}, minOffset: {}, maxOffset: {}, {}",
                        group, topic, queueId, offset, minOffset, maxOffset, e.getMessage());
                } finally {
                    if (bufferConsumeQueue != null) {
                        bufferConsumeQueue.release();
                    }
                }
            }

            if (diskFallRecorded) {
                long fallBehind = maxOffsetPy - maxPhyOffsetPulling;
                brokerStatsManager.recordDiskFallBehindSize(group, topic, queueId, fallBehind);
            }

            long diff = maxOffsetPy - maxPhyOffsetPulling;
            long memory = (long) (StoreUtil.TOTAL_PHYSICAL_MEMORY_SIZE
                * (this.messageStoreConfig.getAccessMessageInMemoryMaxRatio() / 100.0));
            getResult.setSuggestPullingFromSlave(diff > memory);
        }
    } else {
        status = GetMessageStatus.NO_MATCHED_LOGIC_QUEUE;
        nextBeginOffset = nextOffsetCorrection(offset, 0);
    }

    if (GetMessageStatus.FOUND == status) {
        this.storeStatsService.getGetMessageTimesTotalFound().add(1);
    } else {
        this.storeStatsService.getGetMessageTimesTotalMiss().add(1);
    }
    long elapsedTime = this.getSystemClock().now() - beginTime;
    this.storeStatsService.setGetMessageEntireTimeMax(elapsedTime);

    // lazy init no data found.
    if (getResult == null) {
        getResult = new GetMessageResult(0);
    }

    getResult.setStatus(status);
    getResult.setNextBeginOffset(nextBeginOffset);
    getResult.setMaxOffset(maxOffset);
    getResult.setMinOffset(minOffset);
    return getResult;
}

这一步主要是判断队列状态和offset是否正常,根据队列类型从对应的log中取出数据,把数据写到GetMessageResult对象中,返回这个对象,拿到这个对象后,调用pullMessageResultHandler#handle处理这个消息后返回

pullMessageResultHandler消息处理

pullMessageResultHandler的handle方法对GetMessageResult结果进行处理,根据消息类型做对应的处理和写会给消费者

@Override
public RemotingCommand handle(final GetMessageResult getMessageResult,
    final RemotingCommand request,
    final PullMessageRequestHeader requestHeader,
    final Channel channel,
    final SubscriptionData subscriptionData,
    final SubscriptionGroupConfig subscriptionGroupConfig,
    final boolean brokerAllowSuspend,
    final MessageFilter messageFilter,
    RemotingCommand response,
    TopicQueueMappingContext mappingContext,
    long beginTimeMills) {

    //获取PullMessageProcessor
    PullMessageProcessor processor = brokerController.getPullMessageProcessor();
    //计算客户端地址
    final String clientAddress = RemotingHelper.parseChannelRemoteAddr(channel);
    //获取topic配置
    TopicConfig topicConfig = this.brokerController.getTopicConfigManager().selectTopicConfig(requestHeader.getTopic());
    processor.composeResponseHeader(requestHeader, getMessageResult, topicConfig.getTopicSysFlag(),
        subscriptionGroupConfig, response, clientAddress);
    try {
        //执行processor的before hook
        processor.executeConsumeMessageHookBefore(request, requestHeader, getMessageResult, brokerAllowSuspend, response.getCode());
    } catch (AbortProcessException e) {
        response.setCode(e.getResponseCode());
        response.setRemark(e.getErrorMessage());
        return response;
    }

    //重写静态topic的响应
    final PullMessageResponseHeader responseHeader = (PullMessageResponseHeader) response.readCustomHeader();
    RemotingCommand rewriteResult = processor.rewriteResponseForStaticTopic(requestHeader, responseHeader, mappingContext, response.getCode());
    if (rewriteResult != null) {
        response = rewriteResult;
    }
    //更新广播的offset
    processor.updateBroadcastPulledOffset(requestHeader.getTopic(), requestHeader.getConsumerGroup(),
        requestHeader.getQueueId(), requestHeader, channel, response, getMessageResult.getNextBeginOffset());
    //调用processor提交offset
    processor.tryCommitOffset(brokerAllowSuspend, requestHeader, getMessageResult.getNextBeginOffset(),
        clientAddress);
    //根据返回的code进行处理
    switch (response.getCode()) {
        case ResponseCode.SUCCESS:
            //成功

            //更新BrokerStats管理器的计数
            this.brokerController.getBrokerStatsManager().incGroupGetNums(requestHeader.getConsumerGroup(), requestHeader.getTopic(),
                getMessageResult.getMessageCount());

            //更新BrokerStats的大小计数
            this.brokerController.getBrokerStatsManager().incGroupGetSize(requestHeader.getConsumerGroup(), requestHeader.getTopic(),
                getMessageResult.getBufferTotalSize());

            //topic消费计数
            this.brokerController.getBrokerStatsManager().incBrokerGetNums(requestHeader.getTopic(), getMessageResult.getMessageCount());

            //处理Retry 和  DLQ 的主题
            if (!BrokerMetricsManager.isRetryOrDlqTopic(requestHeader.getTopic())) {
                Attributes attributes = BrokerMetricsManager.newAttributesBuilder()
                    .put(LABEL_TOPIC, requestHeader.getTopic())
                    .put(LABEL_CONSUMER_GROUP, requestHeader.getConsumerGroup())
                    .put(LABEL_IS_SYSTEM, TopicValidator.isSystemTopic(requestHeader.getTopic()) || MixAll.isSysConsumerGroup(requestHeader.getConsumerGroup()))
                    .build();
                BrokerMetricsManager.messagesOutTotal.add(getMessageResult.getMessageCount(), attributes);
                BrokerMetricsManager.throughputOutTotal.add(getMessageResult.getBufferTotalSize(), attributes);
            }

            //如果通道无法写入了,就把这个请求result释放  !该行为目的是避免oom
            if (!channelIsWritable(channel, requestHeader)) {
                getMessageResult.release();
                //ignore pull request
                return null;
            }

            //如果这个broker是通过堆内存交换message
            if (this.brokerController.getBrokerConfig().isTransferMsgByHeap()) {
                //转换消息类型
                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));
                //写入body后返回
                response.setBody(r);
                return response;
            } else {
                //否则则使用堆外映射FileRegion然后写入channel
                try {
                    FileRegion fileRegion =
                        new ManyMessageTransfer(response.encodeHeader(getMessageResult.getBufferTotalSize()), getMessageResult);
                    RemotingCommand finalResponse = response;
                    channel.writeAndFlush(fileRegion)
                        .addListener((ChannelFutureListener) future -> {
                            getMessageResult.release();
                            Attributes attributes = RemotingMetricsManager.newAttributesBuilder()
                                .put(LABEL_REQUEST_CODE, RemotingHelper.getRequestCodeDesc(request.getCode()))
                                .put(LABEL_RESPONSE_CODE, RemotingHelper.getResponseCodeDesc(finalResponse.getCode()))
                                .put(LABEL_RESULT, RemotingMetricsManager.getWriteAndFlushResult(future))
                                .build();
                            RemotingMetricsManager.rpcLatency.record(request.getProcessTimer().elapsed(TimeUnit.MILLISECONDS), attributes);
                            if (!future.isSuccess()) {
                                log.error("Fail to transfer messages from page cache to {}", channel.remoteAddress(), future.cause());
                            }
                        });
                } catch (Throwable e) {
                    log.error("Error occurred when transferring messages from page cache", e);
                    getMessageResult.release();
                }
                return null;
            }
        case ResponseCode.PULL_NOT_FOUND:
            //没拉到消息
            final boolean hasSuspendFlag = PullSysFlag.hasSuspendFlag(requestHeader.getSysFlag());
            final long suspendTimeoutMillisLong = hasSuspendFlag ? requestHeader.getSuspendTimeoutMillis() : 0;

            if (brokerAllowSuspend && hasSuspendFlag) {
                long pollingTimeMills = suspendTimeoutMillisLong;
                if (!this.brokerController.getBrokerConfig().isLongPollingEnable()) {
                    pollingTimeMills = this.brokerController.getBrokerConfig().getShortPollingTimeMills();
                }

                String topic = requestHeader.getTopic();
                long offset = requestHeader.getQueueOffset();
                int queueId = requestHeader.getQueueId();
                PullRequest pullRequest = new PullRequest(request, channel, pollingTimeMills,
                    this.brokerController.getMessageStore().now(), offset, subscriptionData, messageFilter);
                this.brokerController.getPullRequestHoldService().suspendPullRequest(topic, queueId, pullRequest);
                return null;
            }
        case ResponseCode.PULL_RETRY_IMMEDIATELY://需要重试
            break;
        case ResponseCode.PULL_OFFSET_MOVED:
            //offset发生改变
            if (this.brokerController.getMessageStoreConfig().getBrokerRole() != BrokerRole.SLAVE
                || this.brokerController.getMessageStoreConfig().isOffsetCheckInSlave()) {
                MessageQueue mq = new MessageQueue();
                mq.setTopic(requestHeader.getTopic());
                mq.setQueueId(requestHeader.getQueueId());
                mq.setBrokerName(this.brokerController.getBrokerConfig().getBrokerName());

                OffsetMovedEvent event = new OffsetMovedEvent();
                event.setConsumerGroup(requestHeader.getConsumerGroup());
                event.setMessageQueue(mq);
                event.setOffsetRequest(requestHeader.getQueueOffset());
                event.setOffsetNew(getMessageResult.getNextBeginOffset());
                log.warn(
                    "PULL_OFFSET_MOVED:correction offset. topic={}, groupId={}, requestOffset={}, newOffset={}, suggestBrokerId={}",
                    requestHeader.getTopic(), requestHeader.getConsumerGroup(), event.getOffsetRequest(), event.getOffsetNew(),
                    responseHeader.getSuggestWhichBrokerId());
            } else {
                responseHeader.setSuggestWhichBrokerId(subscriptionGroupConfig.getBrokerId());
                response.setCode(ResponseCode.PULL_RETRY_IMMEDIATELY);
                log.warn("PULL_OFFSET_MOVED:none correction. topic={}, groupId={}, requestOffset={}, suggestBrokerId={}",
                    requestHeader.getTopic(), requestHeader.getConsumerGroup(), requestHeader.getQueueOffset(),
                    responseHeader.getSuggestWhichBrokerId());
            }

            break;
        default:
            log.warn("[BUG] impossible result code of get message: {}", response.getCode());
            assert false;
    }

    return response;
}

handle成功消息的时候,有个参数是transferMsgByHeap,控制是否通过堆内存去交换消息,如果不启用堆内存去交换消息的话,带来的收益是

  • 减少数据拷贝的过程
  • 减少零字节拷贝的过程

这样在传递效率上会更高,但也存在当堆外内存不够用了得时候,会触发gc和写file导致突发消息大批量超时。