rocketmq系列第二篇【消息消费源码解析】

消息消费的原理对于我们理解消息队列的运行十分有帮助,今天我们来了解rocketmq的push模式消费。

消费全过程:

  1. 消费者启动
  2. 检查配置
  3. 启动客户端netty服务
  4. 开启定时任务:(1)每隔30S定时更新 Topic路由配置 (2)每隔30S清空下线的Broker(Master或Slave),向Broker发送心跳,传递生产者或订阅信息 (3)每隔5S持久化消费进度到远程
  5. 负载均衡开始:获取topic和路由数据
  6. 循环每个topic,按照平均策略,环形策略,队列分配算法或者机房算法(默认是平均策略)给当前consumer计算该topic的对应brokerName的那些队列(一般是0,1,2,3四个队列)进行分配。
  7. 组装拉取请求,(从Broker读取MessageQueue的消费OffSet), 将所有的消息拉取请求放入到消息拉取请求队列
  8. 消费者拉取线程启动,循环从消息拉取队列中获取拉取请求,去请求broker从该topic对应的broker下面的queue,从指定的消费Offset拉取指定数量的消息
  9. broker开始处理: 通过topic,queueId,offset从ConsumeQueue里获取新加入消息的消费信息(消息物理位置offset,消息长度,消息tagsCode),然后循环从CommitLog里提取消息
  10. 然后broker将消息 内容返回给consumer client端
  11. 提取ByteBuffer生成List
  12. 循环处理每一个条数据,进入consumeMessage方法,若消费成功则返回CONSUME_SUCCESS,否则消费失败
  13. 移除消费成功消息,并返回消费的最新进度, 更新本地的offsetTable,对各个队列的offset进行更新,定时任务会每5秒对其进行持久化到broker.

1.消费入口PushConsumer

import java.util.List;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.MessageExt;

public class PushConsumer {

    public static void main(String[] args) throws InterruptedException, MQClientException {
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("CID_JODIE_1");
        consumer.subscribe("TopicTest1", "*");
        consumer.setNamesrvAddr("127.0.0.1:9876");
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                System.out.printf(Thread.currentThread().getName() + " Receive New Messages: " + msgs + "%n");
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        //开启,开始消费消息
        consumer.start();
        System.out.printf("Consumer Started.%n");
    }
}

2.org.apache.rocketmq.client.consumer.DefaultMQPushConsumer#start

3.org.apache.rocketmq.client.impl.consumer.DefaultMQPushConsumerImpl#start

public void start() throws MQClientException {
        switch (this.serviceState) {
            case CREATE_JUST:
                log.info("the consumer [{}] start beginning. messageModel={}, isUnitMode={}", this.defaultMQPushConsumer.getConsumerGroup(),
                    this.defaultMQPushConsumer.getMessageModel(), this.defaultMQPushConsumer.isUnitMode());
                this.serviceState = ServiceState.START_FAILED;

                // 检查配置
                this.checkConfig();

                // Rebalance负载均衡 复制订阅数据
                this.copySubscription();

                // 设置instanceName,为一个字符串化的数字,比如10072
                if (this.defaultMQPushConsumer.getMessageModel() == MessageModel.CLUSTERING) {
                    this.defaultMQPushConsumer.changeInstanceNameToPID();
                }

                // 获取MQClient对象,clientId为ip@instanceName,比如192.168.0.1@10072
                this.mQClientFactory = MQClientManager.getInstance().getAndCreateMQClientInstance(this.defaultMQPushConsumer, this.rpcHook);

                // 设置负载均衡器
                this.rebalanceImpl.setConsumerGroup(this.defaultMQPushConsumer.getConsumerGroup());
                //默认这是消费模式为集群模式,每条消息被同一组的消费者中的一个消费
                //还可以设置为广播模式,每条消息被同一个组的所有消费者都消费一次
                this.rebalanceImpl.setMessageModel(this.defaultMQPushConsumer.getMessageModel());
                //默认是AllocateMessageQueueAveragely,均分策略
                this.rebalanceImpl.setAllocateMessageQueueStrategy(this.defaultMQPushConsumer.getAllocateMessageQueueStrategy());
                this.rebalanceImpl.setmQClientFactory(this.mQClientFactory);

                // 拉取API封装
                this.pullAPIWrapper = new PullAPIWrapper(mQClientFactory, this.defaultMQPushConsumer.getConsumerGroup(), isUnitMode());
                this.pullAPIWrapper.registerFilterMessageHook(filterMessageHookList);

                //生成消费进度处理器,集群模式下消费进度保存在Broker上,因为同一组内的消费者要共享进度;广播模式下进度保存在消费者端
                if (this.defaultMQPushConsumer.getOffsetStore() != null) {
                    this.offsetStore = this.defaultMQPushConsumer.getOffsetStore();
                } else {
                    switch (this.defaultMQPushConsumer.getMessageModel()) {
                        case BROADCASTING:
                            this.offsetStore = new LocalFileOffsetStore(this.mQClientFactory, this.defaultMQPushConsumer.getConsumerGroup());
                            break;
                        case CLUSTERING:
                            this.offsetStore = new RemoteBrokerOffsetStore(this.mQClientFactory, this.defaultMQPushConsumer.getConsumerGroup());
                            break;
                        default:
                            break;
                    }
                }
                //若是广播模式,加载本地的消费进度文件
                this.offsetStore.load();

                // 根据监听是顺序模式还是并发模式来生成相应的ConsumerService
                if (this.getMessageListenerInner() instanceof MessageListenerOrderly) {
                    this.consumeOrderly = true;
                    this.consumeMessageService = new ConsumeMessageOrderlyService(this, (MessageListenerOrderly)this.getMessageListenerInner());
                } else if (this.getMessageListenerInner() instanceof MessageListenerConcurrently) {
                    this.consumeOrderly = false;
                    this.consumeMessageService = new ConsumeMessageConcurrentlyService(this, (MessageListenerConcurrently)this.getMessageListenerInner());
                }
                this.consumeMessageService.start();

                // 设置MQClient对象
                boolean registerOK = mQClientFactory.registerConsumer(this.defaultMQPushConsumer.getConsumerGroup(), this);
                if (!registerOK) {
                    this.serviceState = ServiceState.CREATE_JUST;
                    this.consumeMessageService.shutdown();
                    throw new MQClientException("The consumer group[" + this.defaultMQPushConsumer.getConsumerGroup()
                        + "] has been created before, specify another name please." + FAQUrl.suggestTodo(FAQUrl.GROUP_NAME_DUPLICATE_URL),
                        null);
                }
                mQClientFactory.start();
                log.info("the consumer [{}] start OK.", this.defaultMQPushConsumer.getConsumerGroup());

                // 设置服务状态
                this.serviceState = ServiceState.RUNNING;
                break;
            case RUNNING:
            case START_FAILED:
            case SHUTDOWN_ALREADY:
                throw new MQClientException("The PushConsumer service state not OK, maybe started once, "//
                    + this.serviceState//
                    + FAQUrl.suggestTodo(FAQUrl.CLIENT_SERVICE_NOT_OK),
                    null);
            default:
                break;
        }

        //  从Namesrv获取TopicRouteData,更新TopicPublishInfo和MessageQueue   (在Consumer start时马上调用,之后每隔一段时间调用一次)
        this.updateTopicSubscribeInfoWhenSubscriptionChanged();

        // 向TopicRouteData里的所有Broker发送心跳,注册Consumer/Producer信息到Broker上   (在Consumer start时马上调用,之后每隔一段时间调用一次)
        this.mQClientFactory.sendHeartbeatToAllBrokerWithLock();

        // 唤醒MessageQueue均衡服务,负载均衡后马上开启第一次拉取消息
        this.mQClientFactory.rebalanceImmediately();
    }

4.生成对应的消费线程池,如:ConsumeMessageConcurrentlyService

一般最小消费线程数20,最大64

public ConsumeMessageConcurrentlyService(DefaultMQPushConsumerImpl defaultMQPushConsumerImpl,
                                             MessageListenerConcurrently messageListener) {
        //指定消费者
        this.defaultMQPushConsumerImpl = defaultMQPushConsumerImpl;
        //指定监听器对象
        this.messageListener = messageListener;
        //获取默认的消费者对象
        this.defaultMQPushConsumer = this.defaultMQPushConsumerImpl.getDefaultMQPushConsumer();
        //获取消费者组
        this.consumerGroup = this.defaultMQPushConsumer.getConsumerGroup();

        //指定顺序消息队列
        this.consumeRequestQueue = new LinkedBlockingQueue<>();

        //指定消费连接池
        this.consumeExecutor = new ThreadPoolExecutor(//
            this.defaultMQPushConsumer.getConsumeThreadMin(), //
            this.defaultMQPushConsumer.getConsumeThreadMax(), //
            1000 * 60, //
            TimeUnit.MILLISECONDS, //
            this.consumeRequestQueue, //
            new ThreadFactoryImpl("ConsumeMessageThread_"));

        this.scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl("ConsumeMessageScheduledThread_"));
        this.cleanExpireMsgExecutors = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl("CleanExpireMsgScheduledThread_"));
    }

5.开始拉取消息org.apache.rocketmq.client.impl.factory.MQClientInstance#start

public void start() throws MQClientException {

        synchronized (this) {
            switch (this.serviceState) {
                case CREATE_JUST:
                    this.serviceState = ServiceState.START_FAILED;
                    // If not specified,looking address from name server
                    if (null == this.clientConfig.getNamesrvAddr()) {
                        //如果url未指定,可以通过Http请求从其他处获取
                        this.mQClientAPIImpl.fetchNameServerAddr();
                    }
                    // Start request-response channel
                    this.mQClientAPIImpl.start();
                    // 启动多个定时任务
                    this.startScheduledTask();
                    // Start pull service
                    this.pullMessageService.start();
                    // Start Consumer rebalance service
                    this.rebalanceService.start();
                    //启动内部默认的生产者,用于消费者SendMessageBack,但不会执行MQClientInstance.start(),也就是当前方法不会被执行
                    this.defaultMQProducer.getDefaultMQProducerImpl().start(false);
                    log.info("the client factory [{}] start OK", this.clientId);
                    this.serviceState = ServiceState.RUNNING;
                    break;
                case RUNNING:
                    break;
                case SHUTDOWN_ALREADY:
                    break;
                case START_FAILED:
                    throw new MQClientException("The Factory object[" + this.getClientId() + "] has been created before, and failed.", null);
                default:
                    break;
            }
        }
    }

6.消费者拉取消息线程服务org.apache.rocketmq.client.impl.consumer.PullMessageService

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

        while (!this.isStopped()) {
            try {
                //使用BlockingQueue阻塞队列,当提交了消息拉取请求后,马上执行
                PullRequest pullRequest = this.pullRequestQueue.take();
                if (pullRequest != null) {
                    this.pullMessage(pullRequest);
                }
            } catch (InterruptedException e) {
            } catch (Exception e) {
                log.error("Pull Message Service Run Method exception", e);
            }
        }

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

	/**
     * 拉取消息
     *
     * @param pullRequest 拉取消息请求
     */
    private void pullMessage(final PullRequest pullRequest) {
        // 通过消费者组 组名获取消费者
        final MQConsumerInner consumer = this.mQClientFactory.selectConsumer(pullRequest.getConsumerGroup());
        if (consumer != null) {
            DefaultMQPushConsumerImpl impl = (DefaultMQPushConsumerImpl) consumer;
            impl.pullMessage(pullRequest);
        } else {
            log.warn("No matched consumer for the PullRequest {}, drop it", pullRequest);
        }
    }

7.拉取消息主方法 org.apache.rocketmq.client.impl.consumer.DefaultMQPushConsumerImpl#pullMessage

...
 // 执行拉取。如果拉取请求发生异常时,提交延迟拉取消息请求。
        try {
            this.pullAPIWrapper.pullKernelImpl(//
                pullRequest.getMessageQueue(), // 1
                subExpression, // 2
                subscriptionData.getSubVersion(), // 3
                pullRequest.getNextOffset(), // 4
                this.defaultMQPushConsumer.getPullBatchSize(), // 5
                sysFlag, // 6
                commitOffsetValue, // 7
                BROKER_SUSPEND_MAX_TIME_MILLIS, // 8
                CONSUMER_TIMEOUT_MILLIS_WHEN_SUSPEND, // 9
                CommunicationMode.ASYNC, // 10
                pullCallback// 11
            );
        } catch (Exception e) {
            log.error("pullKernelImpl exception", e);
            this.executePullRequestLater(pullRequest, PULL_TIME_DELAY_MILLS_WHEN_EXCEPTION);
        }
...

8.拉取消息

/**
     * 拉取消息核心方法
     * 拉取消息的开始位置 >= 当前消费进度 ,因为可能消费者还有线程在消费已拉取到的消息
     *
     * @param mq                         消息队列
     * @param subExpression              订阅表达式
     * @param subVersion                 订阅版本号
     * @param offset                     下次请求从ConsumeQueue拉取的开始位置
     * @param maxNums                    批量拉 取消息数量
     * @param sysFlag                    拉取系统标识
     * @param commitOffset               提交ConsumeQueue的消费进度
     * @param brokerSuspendMaxTimeMillis broker挂起请求最大时间
     * @param timeoutMillis              请求broker超时时间
     * @param communicationMode          通讯模式
     * @param pullCallback               拉取回调
     * @return 拉取消息结果。只有通讯模式为同步时,才返回结果,否则返回null。
     * @throws MQClientException 当寻找不到 broker 时,或发生其他client异常
     * @throws RemotingException 当远程调用发生异常时
     * @throws MQBrokerException 当 broker 发生异常时。只有通讯模式为同步时才会发生该异常。
     * @throws InterruptedException 当发生中断异常时
     */
    protected PullResult pullKernelImpl(
        final MessageQueue mq,
        final String subExpression,
        final long subVersion,
        final long offset,
        final int maxNums,
        final int sysFlag,
        final long commitOffset,
        final long brokerSuspendMaxTimeMillis,
        final long timeoutMillis,
        final CommunicationMode communicationMode,
        final PullCallback pullCallback
    ) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
        // 获取Broker信息
        FindBrokerResult findBrokerResult =
            this.mQClientFactory.findBrokerAddressInSubscribe(mq.getBrokerName(), this.recalculatePullFromWhichNode(mq), false);
        if (null == findBrokerResult) {
            this.mQClientFactory.updateTopicRouteInfoFromNameServer(mq.getTopic());
            findBrokerResult = this.mQClientFactory.findBrokerAddressInSubscribe(mq.getBrokerName(), this.recalculatePullFromWhichNode(mq), false);
        }

        // 请求拉取消息
        if (findBrokerResult != null) {
            int sysFlagInner = sysFlag;

            if (findBrokerResult.isSlave()) {  //如果是从Slave拉取消息,则不提交消费进度
                sysFlagInner = PullSysFlag.clearCommitOffsetFlag(sysFlagInner);
            }

            PullMessageRequestHeader requestHeader = new PullMessageRequestHeader();
            requestHeader.setConsumerGroup(this.consumerGroup);
            requestHeader.setTopic(mq.getTopic());
            requestHeader.setQueueId(mq.getQueueId());
            requestHeader.setQueueOffset(offset);
            requestHeader.setMaxMsgNums(maxNums);
            requestHeader.setSysFlag(sysFlagInner);
            requestHeader.setCommitOffset(commitOffset);
            requestHeader.setSuspendTimeoutMillis(brokerSuspendMaxTimeMillis);
            requestHeader.setSubscription(subExpression);
            requestHeader.setSubVersion(subVersion);

            // 若订阅topic使用过滤类,使用filtersrv获取消息
            String brokerAddr = findBrokerResult.getBrokerAddr();
            if (PullSysFlag.hasClassFilterFlag(sysFlagInner)) {
                brokerAddr = computPullFromWhichFilterServer(mq.getTopic(), brokerAddr);
            }

            PullResult pullResult = this.mQClientFactory.getMQClientAPIImpl().pullMessage(
                brokerAddr,
                requestHeader,
                timeoutMillis,
                communicationMode,
                pullCallback);

            return pullResult;
        }

        // Broker信息不存在,则抛出异常
        throw new MQClientException("The broker[" + mq.getBrokerName() + "] not exist", null);
    }
/**
     * 拉取消息
     *
     * @param addr              broker地址
     * @param requestHeader     请求头
     * @param timeoutMillis     请求超时
     * @param communicationMode 通讯方式
     * @param pullCallback      回调
     * @return 消息。只有通讯模式为同步时,才返回结果,否则返回null。
     * @throws RemotingException 当远程调用发生异常时
     * @throws MQBrokerException 当 broker 发生异常时。只有通讯模式为同步时才会发生该异常。
     * @throws InterruptedException 当发生中断异常时
     */
    public PullResult pullMessage(//
                                  final String addr, //
                                  final PullMessageRequestHeader requestHeader, //
                                  final long timeoutMillis, //
                                  final CommunicationMode communicationMode, //
                                  final PullCallback pullCallback//
    ) throws RemotingException, MQBrokerException, InterruptedException {
        RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.PULL_MESSAGE, requestHeader);

        switch (communicationMode) {
            case ONEWAY:
                assert false;
                return null;
            case ASYNC:
                this.pullMessageAsync(addr, request, timeoutMillis, pullCallback);
                return null;
            case SYNC:
                return this.pullMessageSync(addr, request, timeoutMillis);
            default:
                assert false;
                break;
        }

        return null;
    }
public void invokeAsyncImpl(final Channel channel, final RemotingCommand request, final long timeoutMillis,
        final InvokeCallback invokeCallback)
        throws InterruptedException, RemotingTooMuchRequestException, RemotingTimeoutException, RemotingSendRequestException {
        final int opaque = request.getOpaque();
        boolean acquired = this.semaphoreAsync.tryAcquire(timeoutMillis, TimeUnit.MILLISECONDS);
        if (acquired) {
            final SemaphoreReleaseOnlyOnce once = new SemaphoreReleaseOnlyOnce(this.semaphoreAsync);

            final ResponseFuture responseFuture = new ResponseFuture(opaque, timeoutMillis, invokeCallback, once);
            this.responseTable.put(opaque, responseFuture);
            try {
                channel.writeAndFlush(request).addListener(new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture f) throws Exception {
                        if (f.isSuccess()) {
                            responseFuture.setSendRequestOK(true);
                            return;
                        } else {
                            responseFuture.setSendRequestOK(false);
                        }

                        responseFuture.putResponse(null);
                        responseTable.remove(opaque);
                        try {
                            executeInvokeCallback(responseFuture);
                        } catch (Throwable e) {
                            PLOG.warn("excute callback in writeAndFlush addListener, and callback throw", e);
                        } finally {
                            responseFuture.release();
                        }

                        PLOG.warn("send a request command to channel <{}> failed.", RemotingHelper.parseChannelRemoteAddr(channel));
                    }
                });
            } catch (Exception e) {
                responseFuture.release();
                PLOG.warn("send a request command to channel <" + RemotingHelper.parseChannelRemoteAddr(channel) + "> Exception", e);
                throw new RemotingSendRequestException(RemotingHelper.parseChannelRemoteAddr(channel), e);
            }
        } else {
            String info =
                String.format("invokeAsyncImpl tryAcquire semaphore timeout, %dms, waiting thread nums: %d semaphoreAsyncValue: %d", //
                    timeoutMillis, //
                    this.semaphoreAsync.getQueueLength(), //
                    this.semaphoreAsync.availablePermits()//
                );
            PLOG.warn(info);
            throw new RemotingTooMuchRequestException(info);
        }
    }

9.filtersrv 处理请求

@Override
    public RemotingCommand processRequest(ChannelHandlerContext ctx, RemotingCommand request) throws Exception {
        if (log.isDebugEnabled()) {
            log.debug("receive request, {} {} {}",
                    request.getCode(),
                    RemotingHelper.parseChannelRemoteAddr(ctx.channel()),
                    request);
        }

        switch (request.getCode()) {
            case RequestCode.REGISTER_MESSAGE_FILTER_CLASS:
                return registerMessageFilterClass(ctx, request);
            case RequestCode.PULL_MESSAGE:
                return pullMessageForward(ctx, request);
        }

        return null;
    }

10.filtersrv拉取消息

/**
     * 拉取消息
     *
     * @param ctx     拉取消息context
     * @param request 拉取消息请求
     * @return 响应
     * @throws Exception 当发生异常时
     */
    private RemotingCommand pullMessageForward(final ChannelHandlerContext ctx, final RemotingCommand request) throws Exception {
        final RemotingCommand response = RemotingCommand.createResponseCommand(PullMessageResponseHeader.class);
        final PullMessageResponseHeader responseHeader = (PullMessageResponseHeader) response.readCustomHeader();
        final PullMessageRequestHeader requestHeader =
                (PullMessageRequestHeader) request.decodeCommandCustomHeader(PullMessageRequestHeader.class);

        final FilterContext filterContext = new FilterContext();
        filterContext.setConsumerGroup(requestHeader.getConsumerGroup());

        response.setOpaque(request.getOpaque());

        DefaultMQPullConsumer pullConsumer = this.filtersrvController.getDefaultMQPullConsumer();

        // 校验Topic过滤类是否完整
        final FilterClassInfo findFilterClass = this.filtersrvController.getFilterClassManager().findFilterClass(requestHeader.getConsumerGroup(), requestHeader.getTopic());
        if (null == findFilterClass) {
            response.setCode(ResponseCode.SYSTEM_ERROR);
            response.setRemark("Find Filter class failed, not registered");
            return response;
        }
        if (null == findFilterClass.getMessageFilter()) {
            response.setCode(ResponseCode.SYSTEM_ERROR);
            response.setRemark("Find Filter class failed, registered but no class");
            return response;
        }

        // 设置下次请求从 Broker主节点。
        responseHeader.setSuggestWhichBrokerId(MixAll.MASTER_ID);

        MessageQueue mq = new MessageQueue();
        mq.setTopic(requestHeader.getTopic());
        mq.setQueueId(requestHeader.getQueueId());
        mq.setBrokerName(this.filtersrvController.getBrokerName());
        long offset = requestHeader.getQueueOffset();
        int maxNums = requestHeader.getMaxMsgNums();

        final PullCallback pullCallback = new PullCallback() {

            @Override
            public void onSuccess(PullResult pullResult) {
                responseHeader.setMaxOffset(pullResult.getMaxOffset());
                responseHeader.setMinOffset(pullResult.getMinOffset());
                responseHeader.setNextBeginOffset(pullResult.getNextBeginOffset());
                response.setRemark(null);

                switch (pullResult.getPullStatus()) {
                    case FOUND:
                        response.setCode(ResponseCode.SUCCESS);

                        List<MessageExt> msgListOK = new ArrayList<MessageExt>();
                        try {
                            for (MessageExt msg : pullResult.getMsgFoundList()) {
                                // 使用过滤类过滤消息
                                boolean match = findFilterClass.getMessageFilter().match(msg, filterContext);
                                if (match) {
                                    msgListOK.add(msg);
                                }
                            }

                            if (!msgListOK.isEmpty()) {
                                returnResponse(requestHeader.getConsumerGroup(), requestHeader.getTopic(), ctx, response, msgListOK);
                                return;
                            } else {
                                response.setCode(ResponseCode.PULL_RETRY_IMMEDIATELY);
                            }
                        } catch (Throwable e) {
                            final String error =
                                    String.format("do Message Filter Exception, ConsumerGroup: %s Topic: %s ",
                                            requestHeader.getConsumerGroup(), requestHeader.getTopic());
                            log.error(error, e);

                            response.setCode(ResponseCode.SYSTEM_ERROR);
                            response.setRemark(error + RemotingHelper.exceptionSimpleDesc(e));
                            returnResponse(requestHeader.getConsumerGroup(), requestHeader.getTopic(), ctx, response, null);
                            return;
                        }

                        break;
                    case NO_MATCHED_MSG:
                        response.setCode(ResponseCode.PULL_RETRY_IMMEDIATELY);
                        break;
                    case NO_NEW_MSG:
                        response.setCode(ResponseCode.PULL_NOT_FOUND);
                        break;
                    case OFFSET_ILLEGAL:
                        response.setCode(ResponseCode.PULL_OFFSET_MOVED);
                        break;
                    default:
                        break;
                }

                returnResponse(requestHeader.getConsumerGroup(), requestHeader.getTopic(), ctx, response, null);
            }

            @Override
            public void onException(Throwable e) {
                response.setCode(ResponseCode.SYSTEM_ERROR);
                response.setRemark("Pull Callback Exception, " + RemotingHelper.exceptionSimpleDesc(e));
                returnResponse(requestHeader.getConsumerGroup(), requestHeader.getTopic(), ctx, response, null);
                return;
            }
        };

        // 拉取消息
        pullConsumer.pullBlockIfNotFound(mq, null, offset, maxNums, pullCallback);
        return null;
    }

11. broker内部拉取消息方法

org.apache.rocketmq.broker.processor.PullMessageProcessor#processRequest(io.netty.channel.Channel, org.apache.rocketmq.remoting.protocol.RemotingCommand, boolean)

 /**
     * 处理拉取消息请求,返回响应
     *
     * @param channel            channel
     * @param request            请求
     * @param brokerAllowSuspend broker是否允许挂起
     * @return 响应
     * @throws RemotingCommandException 当解析请求发生异常时
     */
    private RemotingCommand processRequest(final Channel channel, RemotingCommand request, boolean brokerAllowSuspend)
            throws RemotingCommandException {
            ...
            // 通过topic,queueId,offset从ConsumeQueue里获取新加入消息的消费信息,然后从CommitLog里提取消息
        final GetMessageResult getMessageResult = this.brokerController.getMessageStore().getMessage(requestHeader.getConsumerGroup(), requestHeader.getTopic(),
                requestHeader.getQueueId(), requestHeader.getQueueOffset(), requestHeader.getMaxMsgNums(), subscriptionData);
            ...

12. messageStore获取消息

/**
     * 通过topic,queueId,offset从ConsumeQueue里获取新加入消息的消费信息,然后从CommitLog里提取消息
     * 从ConsumeQueue里提取Message时会根据tagsCode过滤不符合的Message
     * 提取的总数(32)条消息都是符合tag的
     *
     * @param group            消费分组
     * @param topic            主题
     * @param queueId          队列编号
     * @param offset           ConsumeQueue Offset
     * @param maxMsgNums       消息数量  32
     * @param subscriptionData 订阅信息
     * @return 消息结果
     */
    @Override
    public GetMessageResult getMessage(final String group, final String topic, final int queueId, final long offset, final int maxMsgNums,
                                       final SubscriptionData subscriptionData) {
        ...
		 // 获取消费队列(里面存放了队列的索引信息)
        ConsumeQueue consumeQueue = findConsumeQueue(topic, queueId);
        if (consumeQueue != null) {
        ...
        // 获取队列的索引信息
        SelectMappedBufferResult bufferConsumeQueue = consumeQueue.getIndexBuffer(offset);
                if (bufferConsumeQueue != null) {
                ...
                for (; i < bufferConsumeQueue.getSize() && i < maxFilterMessageCount; i += ConsumeQueue.CQ_STORE_UNIT_SIZE) {
                            long offsetPy = bufferConsumeQueue.getByteBuffer().getLong(); // 消息物理位置offset
                            int sizePy = bufferConsumeQueue.getByteBuffer().getInt(); // 消息长度
                            long tagsCode = bufferConsumeQueue.getByteBuffer().getLong(); // 消息tagsCode
                      ...

							// 判断消息是否在订阅的tag里
                            if (this.messageFilter.isMessageMatched(subscriptionData, tagsCode)) {
                                // 从commitLog获取对应消息ByteBuffer(这里才是真正的获取消息)
                                SelectMappedBufferResult selectResult = this.commitLog.getMessage(offsetPy, sizePy);
                                if (selectResult != null) {
                                    this.storeStatsService.getGetMessageTransferedMsgCount().incrementAndGet();
                                    getResult.addMessage(selectResult);
                                    status = GetMessageStatus.FOUND;
                                    nextPhyFileStartOffset = Long.MIN_VALUE;
                                } else {
                                    // 从commitLog无法读取到消息,说明该消息对应的文件(MappedFile)已经删除,计算下一个MappedFile的起始位置
                                    if (getResult.getBufferTotalSize() == 0) {
                                        status = GetMessageStatus.MESSAGE_WAS_REMOVING;
                                    }
                                    nextPhyFileStartOffset = this.commitLog.rollNextFile(offsetPy);
                                }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值