🚀 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 的“工作原理”,为性能调优、问题排查与自定义扩展打下坚实基础。
RocketMQ Consumer四大核心流程源码解析
233

被折叠的 条评论
为什么被折叠?



