简述RocketMQ消息拉取过程

本文详细解析了RocketMQ中DefaultMQPushConsumer和DefaultLitePullConsumer的消息拉取过程,从消费者启动拉消息线程、维护消息请求队列、从Broker拉取消息,到消息的消费、流量控制和顺序消息处理等环节,揭示了消息拉取的持续性和闭环机制。
摘要由CSDN通过智能技术生成

前言

相信每一个使用RocketMQ的开发工程师都想了解一下消息是如何被拉到消费端的,消费者在消息拉取过程中都做了什么操作?这些疑问将在接下来的这篇文章中给大家一一解开;

DefaultMQPushConsumer拉消息

RocketMQ有两种类型的消费者,一种是DefaultLitePullConsumer主动拉的模式,另一种是DefaultMQPushConsumer被动接收的模式,我们先来介绍DefaultMQPushConsumer是如何拉消息的。我们需要从MQClientInstance类作为入口,找到pullMessageService,它才是负责从Broker拉取消息的;

  • 启动拉消息线程

在消费者调用start()方法时,我们最终可以发现MQClientInstance.start()方法也被调用了,在里面我们可以发现这样一段代码:

// Start pull service
this.pullMessageService.start();
复制代码

最终我们发现pullMessageService其实是ServiceThread的子类,ServiceThread又实现了Runnable,说白了pullMessageService就是一个任务,pullMessageService.start()源码我们可以看一下:

    public void start() {
        log.info("Try to start service thread:{} started:{} lastThread:{}", getServiceName(), started.get(), thread);
        if (!started.compareAndSet(false, true)) {
            return;
        }
        stopped = false;
        // 把pullMessageService作为线程任务
        this.thread = new Thread(this, getServiceName());
        this.thread.setDaemon(isDaemon);
        this.thread.start();
    }
复制代码

这里面就是启动了一个线程,然后这个线程的任务就是pullMessageService本身,所以我们需要重点关注run()方法的实现:

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

        while (!this.isStopped()) {
            try {
                // 从阻塞队列中取MessageRequest
                MessageRequest messageRequest = this.messageRequestQueue.take();
                // 根据消息请求模式拉消息
                if (messageRequest.getMessageRequestMode() == MessageRequestMode.POP) {
                    this.popMessage((PopRequest)messageRequest);
                } else {
                    this.pullMessage((PullRequest)messageRequest);
                }
            } catch (InterruptedException ignored) {
            } catch (Exception e) {
                log.error("Pull Message Service Run Method exception", e);
            }
        }
​
        log.info(this.getServiceName() + " service end");
    }
复制代码
  • 维护消息请求队列

在DefaultMQPushConsumer中,当重平衡任务启动后,每个消费者实例将分配到对应的MessageQueue,然后就会创建对应的MessageRequest放进messageRequestQueue中,相关代码可查看
RebalanceImpl.updateMessageQueueAssignment()方法,下面贴出相关的代码片段:

boolean allMQLocked = true;
List<PullRequest> pullRequestList = new ArrayList<PullRequest>();
for (MessageQueue mq : mq2PushAssignment.keySet()) {
    if (!this.processQueueTable.containsKey(mq)) {
        if (isOrder && !this.lock(mq)) {
            log.warn("doRebalance, {}, add a new mq failed, {}, because lock failed", consumerGroup, mq);
            allMQLocked = false;
            continue;
        }
        this.removeDirtyOffset(mq);
        ProcessQueue pq = createProcessQueue();
        pq.setLocked(true);
        long nextOffset = -1L;
        try {
            nextOffset = this.computePullFromWhereWithException(mq);
        } catch (Exception e) {
            log.info("doRebalance, {}, compute offset failed, {}", consumerGroup, mq);
            continue;
        }
        if (nextOffset >= 0) {
            ProcessQueue pre = this.processQueueTable.putIfAbsent(mq, pq);
            if (pre != null) {
                log.info("doRebalance, {}, mq already exists, {}", consumerGroup, mq);
            } else {
                log.info("doRebalance, {}, add a new mq, {}", consumerGroup, mq);
                PullRequest pullRequest = new PullRequest();
                pullRequest.setConsumerGroup(consumerGroup);
                pullRequest.setNextOffset(nextOffset);
                pullRequest.setMessageQueue(mq);
                pullRequest.setProcessQueue(pq);
                pullRequestList.add(pullRequest);
                changed = true;
            }
        } else {
            log.warn("doRebalance, {}, add new mq failed, {}", consumerGroup, mq);
        }
    }
}
if (!allMQLocked) {
    mQClientFactory.rebalanceLater(500);
}
this.dispatchPullRequest(pullRequestList, 500);
复制代码

这一段代码就是根据重平衡后生成了pullRequest并放进了messageRequestQueue请求队列中,同样的popRequest也是和上面代码类似:

List<PopRequest> popRequestList = new ArrayList<PopRequest>();
for (MessageQueue mq : mq2PopAssignment.keySet()) {
    if (!this.popProcessQueueTable.containsKey(mq)) {
        PopProcessQueue pq = createPopProcessQueue();
        PopProcessQueue pre = this.popProcessQueueTable.putIfAbsent(mq, pq);
        if (pre != null) {
            log.info("doRebalance, {}, mq pop already exists, {}", consumerGroup, mq);
        } else {
            log.info("doRebalance, {}, add a new pop mq, {}", consumerGroup, mq);
            PopRequest popRequest = new PopRequest();
            popRequest.setTopic(topic);
            popRequest.setConsumerGroup(consumerGroup);
            popRequest.setMessageQueue(mq);
            popRequest.setPopProcessQueue(pq);
            popRequest.setInitMode(getConsumeInitMode());
            popRequestList.add(popRequest);
            changed = true;
        }
    }
}
this.dispatchPopPullRequest(popRequestList, 500);
复制代码

最终都是调用dispatchPullRequest()和dispatchPopPullRequest()把请求放进阻塞队列中:

    @Override
    public void dispatchPullRequest(final List<PullRequest> pullRequestList, final long delay) {
        for (PullRequest pullRequest : pullRequestList) {
            if (delay <= 0) {
               this.defaultMQPushConsumerImpl.executePullRequestImmediately(pullRequest);
            } else {
                this.defaultMQPushConsumerImpl.executePullRequestLater(pullRequest, delay);
            }
        }
    }
​
    @Override
    public void dispatchPopPullRequest(final List<PopRequest> pullRequestList, final long delay) {
        for (PopRequest pullRequest : pullRequestList) {
            if (delay <= 0) {
                this.defaul
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值