RocketMQ Consumer:消息拉取、消费、负载均衡(Rebalance)、Offset 提交 源码详解

RocketMQ Consumer四大核心流程源码解析

🚀 RocketMQ Consumer:消息拉取、消费、负载均衡(Rebalance)、Offset 提交 源码详解

在 RocketMQ 中,Consumer 是消息的“处理引擎”,负责从 Broker 拉取消息、并发/顺序消费、负载均衡与 Offset 管理。
它通过 长轮询 + Pull 模式 实现“伪推送”,并通过 Rebalance 机制 实现高可用与并行处理。

本文将深入 RocketMQ 4.9+ 源码,从源码层面解析 Consumer 的四大核心流程:

  • 消息拉取(Pull)
  • 消息消费(Consume)
  • 负载均衡(Rebalance)
  • Offset 提交(Commit)

一、源码环境准备

  • 源码地址:https://github.com/apache/rocketmq
  • 分支release-4.9.4
  • 核心模块
    • client:Consumer 客户端逻辑
    • common:消息模型、路由信息
    • remoting:网络通信(Netty)

二、1. 消息拉取(Message Pull)

📌 拉取模式:

RocketMQ 所有消费底层都是 Pull 模式,所谓的“Push”是 SDK 封装的长轮询。

🔧 核心类:PullAPIWrapper + PullMessageService

// DefaultMQPushConsumerImpl.java
this.pullMessageService.start(); // 启动拉取服务

🔧 拉取入口:PullMessageService#run

// PullMessageService.java
public void run() {
    while (!this.isStopped()) {
        try {
            // 从阻塞队列中获取拉取请求
            PullRequest pullRequest = this.pullRequestQueue.take();

            // 执行拉取
            this.pullMessage(pullRequest);
        } catch (Exception e) {
            log.error("PullMessageService Exception", e);
        }
    }
}

🔧 拉取核心:DefaultMQPushConsumerImpl#pullMessage

// DefaultMQPushConsumerImpl.java
private void pullMessage(final PullRequest pullRequest) {
    // 1. 获取 Broker 地址
    String brokerAddr = this.mQClientFactory.findBrokerAddressInSubscribe(pullRequest.getBrokerName());

    // 2. 构建拉取请求
    PullMessageRequestHeader requestHeader = new PullMessageRequestHeader();
    requestHeader.setConsumerGroup(this.defaultMQPushConsumer.getConsumerGroup());
    requestHeader.setTopic(pullRequest.getMessageQueue().getTopic());
    requestHeader.setQueueId(pullRequest.getMessageQueue().getQueueId());
    requestHeader.setQueueOffset(pullRequest.getNextOffset());

    // 3. Netty 调用(长轮询)
    RemotingCommand response = this.remotingClient.invokeSync(brokerAddr, request, timeoutMillis);

    // 4. 处理响应
    switch (response.getCode()) {
        case ResponseCode.SUCCESS:
            this.consumeMessageService.submitConsumeRequest(
                getMessageList(response), // 消息列表
                processQueue,
                messageQueue,
                true
            );
            break;
        case ResponseCode.PULL_RETRY_IMMEDIATELY:
            // 无消息 → 重新放入队列,立即重试
            this.executePullRequestLater(pullRequest, 0);
            break;
        case ResponseCode.PULL_OFFSET_MOVED:
            // Offset 被重置 → 重新拉取
            this.correctTagsOffset(pullRequest);
            break;
    }
}

🔧 长轮询机制:Broker 挂起请求

// PullMessageProcessor.java
if (getMessageResult.getStatus() == GetMessageStatus.NO_MESSAGE_IN_QUEUE) {
    if (longPolling) {
        // 挂起请求(最长 15 秒)
        PullRequest pullRequest = new PullRequest(...);
        this.brokerController.getPullRequestHoldService().suspendPullRequest(
            requestHeader.getTopic(), requestHeader.getQueueId(), pullRequest);
        return null; // 不立即返回
    }
}

✅ 实现“近实时”推送效果。


三、2. 消息消费(Message Consume)

📌 两种消费模式:

  • MessageListenerConcurrently:并发消费(高吞吐)
  • MessageListenerOrderly:顺序消费(单线程)

🔧 消费入口:ConsumeMessageConcurrentlyService#submitConsumeRequest

// ConsumeMessageConcurrentlyService.java
public void submitConsumeRequest(
    final List<MessageExt> msgs,
    final ProcessQueue processQueue,
    final MessageQueue messageQueue,
    final boolean dispathToConsume) {

    // 提交到消费线程池
    this.consumeExecutor.submit(new Runnable() {
        @Override
        public void run() {
            // 执行消费逻辑
            status = listener.consumeMessage(msgs, context);
            // 提交 Offset
            ConsumeMessageConcurrentlyService.this.processConsumeResult(status, context, msgs);
        }
    });
}

🔧 顺序消费:ConsumeMessageOrderlyService

// ConsumeMessageOrderlyService.java
public void submitConsumeRequest(
    final List<MessageExt> msgs,
    final ProcessQueue processQueue,
    final MessageQueue messageQueue,
    final boolean dispathToConsume) {

    // 单线程处理
    this.consumeOrderlyExecutor.submit(new Runnable() {
        @Override
        public void run() {
            // 加锁(保证顺序)
            processQueue.getLockConsume().lock();
            try {
                status = listener.consumeMessage(msgs, context);
            } finally {
                processQueue.getLockConsume().unlock();
            }
            // 处理结果
            processConsumeResult(status, context, msgs);
        }
    });
}

✅ 顺序消费通过 ReentrantLock 保证单线程处理。


四、3. 负载均衡(Rebalance)

📌 触发时机:

  • Consumer 启动
  • Consumer 增减
  • Topic Queue 数变化
  • 每 20 秒定时检查

🔧 核心类:RebalanceService

// RebalanceService.java
public void run() {
    while (!this.isStopped()) {
        this.waitForRunning(waitInterval);
        this.mqClientFactory.doRebalance();
    }
}

🔧 Rebalance 流程:RebalanceImpl#doRebalance

// RebalanceImpl.java
public void doRebalance(final boolean isOrder) {
    // 1. 获取当前 Topic 的订阅信息
    Map<String, SubscriptionData> subTable = this.getSubscriptionInner();

    for (String topic : subTable.keySet()) {
        // 2. 分配 Queue
        this.rebalanceByTopic(topic, isOrder);
    }
}

private void rebalanceByTopic(final String topic, boolean isOrder) {
    // 3. 获取 Topic 的所有 Queue
    Set<MessageQueue> mqSet = this.topicSubscribeInfoTable.get(topic);

    // 4. 获取当前 Consumer Group 的所有实例
    List<String> cidAll = this.mQClientFactory.findConsumerIdList(topic, consumerGroup);

    // 5. 使用负载策略分配 Queue
    AllocateMessageQueueStrategy strategy = this.allocateMessageQueueStrategy;
    List<MessageQueue> allocateResult = strategy.allocate(
        this.consumerGroup,
        this.mQClientFactory.getClientId(),
        mqSet,
        cidAll);

    // 6. 更新本地分配结果
    this.updateProcessQueueTable(topic, allocateResult);
}

📌 负载策略(默认):AllocateMessageQueueAveragely

// AllocateMessageQueueAveragely.java
public List<MessageQueue> allocate(String consumerGroup, String currentCID, List<MessageQueue> mqAll, List<String> cidAll) {
    int index = cidAll.indexOf(currentCID);
    int mod = mqAll.size() % cidAll.size();
    int averageSize = mqAll.size() <= mod ? 1 : (mod > 0 && index < mod ? mqAll.size() / cidAll.size() + 1 : mqAll.size() / cidAll.size());
    int startIndex = (mod > 0 && index < mod) ? index * averageSize : index * averageSize + mod;
    int endIndex = startIndex + averageSize;
    return ListUtils.subList(mqAll, startIndex, endIndex);
}

✅ 平均分配 Queue,实现负载均衡。


五、4. Offset 提交(Commit Offset)

📌 两种提交方式:

  • 自动提交:每隔 5 秒持久化一次
  • 手动提交:开发者调用 context.ack()

🔧 自动提交:OffsetStore + 定时任务

// DefaultMQPushConsumerImpl.java
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
    @Override
    public void run() {
        try {
            // 持久化所有 Offset
            MQConsumerInner impl = DefaultMQPushConsumerImpl.this;
            impl.persistAllConsumerOffset();
        } catch (Exception e) {
            log.error("persistAllConsumerOffset exception", e);
        }
    }
}, 1000 * 5, 1000 * 5, TimeUnit.MILLISECONDS);

🔧 提交实现:RemoteBrokerOffsetStore#updateOffset

// RemoteBrokerOffsetStore.java
public void updateOffset(MessageQueue mq, long offset, boolean increaseOnly) {
    // 更新本地内存
    this.offsetTable.put(mq, offset);

    // 异步提交到 Broker
    this.mQClientFactory.getMQClientAPIImpl().updateConsumerOffsetOneway(
        brokerAddr, consumerGroup, mq.getTopic(), mq.getQueueId(), offset, 1000 * 5);
}

🔧 手动提交:ConsumeConcurrentlyContext#ack

// ConsumeConcurrentlyContext.java
public void ack() {
    // 标记已处理
    this.processQueue.getMsgCount().addAndGet(-msgs.size());
    // 更新 Offset
    this.processQueue.commit();
}

✅ 手动提交更精确,推荐关键业务使用。


六、总结:四大流程源码链路

1. 消息拉取

PullMessageService → 从队列获取 PullRequest
   ↓
PullAPIWrapper → 构建请求 → Netty 调用
   ↓
Broker 长轮询挂起或返回消息
   ↓
提交给消费线程

2. 消息消费

ConsumeMessageService → 提交到线程池
   ↓
MessageListener.consumeMessage()
   ↓
处理结果 → 提交 Offset

3. 负载均衡(Rebalance)

RebalanceService 每 20s 触发
   ↓
获取 Topic Queue 列表 + Consumer 列表
   ↓
调用 AllocateMessageQueueStrategy 分配
   ↓
更新本地 ProcessQueue 分配

4. Offset 提交

自动:定时任务 → updateConsumerOffsetOneway
   ↓
手动:context.ack() → commit() → updateOffset
   ↓
Broker 持久化到 __consumer_offsets

✅ 总结

功能核心类说明
消息拉取PullMessageService长轮询实现“伪推送”
消息消费ConsumeMessageService并发/顺序消费支持
负载均衡RebalanceImpl定时 Rebalance + 策略分配
Offset 提交RemoteBrokerOffsetStore自动/手动提交到 Broker

🚀 一句话总结:
RocketMQ Consumer 的核心是 “Pull + Rebalance + Offset 管理”
通过 长轮询 实现高效拉取,
通过 Rebalance 实现负载均衡,
通过 Offset 提交 保障不重复消费,
构建了一个高并发、高可用的消息消费系统。

掌握这些源码逻辑,你就能深入理解 Consumer 的“工作原理”,为性能调优、问题排查与自定义扩展打下坚实基础。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值