RocketMQ提供了两种消息拉取模式,Pull模式(主动拉取)和 Push模式(长轮询)。
一、消息拉取模式分类
1. Pull模式(主动拉取)
-
特点:消费者主动向Broker发送请求拉取消息
-
实现类 :
DefaultMQPullConsumer
-
优点 :
1、消费速率完全由消费者控制
2、适合需要精确控制消费节奏的场景
-
缺点 :
1、需要自行管理消费位点(offset)
2、实现相对复杂
2. Push模式(长轮询)
-
特点:底层基于Pull模式封装,对用户表现为推送体验
-
实现类 :
DefaultMQPushConsumer
-
优点 :
1、使用简单,无需管理offset
2、实时性好(基于长轮询)
- 缺点:消费速率由RocketMQ控制
二、Pull模式详细实现
1. 核心代码示例
import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
import org.apache.rocketmq.client.consumer.PullResult;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageQueue;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class PullConsumerDemo {
private static final Map<MessageQueue, Long> OFFSET_TABLE = new HashMap<>();
public static void main(String[] args) throws Exception {
// 1. 创建消费者实例
DefaultMQPullConsumer consumer = new DefaultMQPullConsumer("pull_consumer_group");
consumer.setNamesrvAddr("127.0.0.1:9876");
consumer.start();
// 2. 获取Topic下的所有队列
Set<MessageQueue> mqs = consumer.fetchSubscribeMessageQueues("TestTopic");
// 3. 遍历队列拉取消息
for (MessageQueue mq : mqs) {
System.out.println("开始从队列 " + mq + " 拉取消息");
// 4. 循环拉取消息
while (true) {
try {
// 获取当前队列的消费位点
long offset = consumer.fetchConsumeOffset(mq, false);
if (offset < 0) offset = 0;
// 拉取消息(每次最多32条)
PullResult pullResult = consumer.pull(mq, "*", offset, 32);
// 处理拉取结果
switch (pullResult.getPullStatus()) {
case FOUND: // 找到消息
for (MessageExt msg : pullResult.getMsgFoundList()) {
System.out.println("收到消息: " + new String(msg.getBody()));
}
// 更新消费位点
long nextOffset = pullResult.getNextBeginOffset();
consumer.updateConsumeOffset(mq, nextOffset);
break;
case NO_NEW_MSG: // 没有新消息
System.out.println("没有新消息");
Thread.sleep(1000); // 暂停1秒
break;
case NO_MATCHED_MSG: // 没有匹配消息
case OFFSET_ILLEGAL: // 位点非法
break;
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
2. Pull模式关键点
- 位点管理 :需要自行调用
fetchConsumeOffset
和updateConsumeOffset
管理消费进度 - 队列遍历:需要获取Topic下所有队列并逐个处理
- 状态处理 :需要处理
FOUND
、NO_NEW_MSG
等不同拉取状态 - 流控:通过调整拉取间隔和批量大小控制消费速率
三、Push模式详细实现
1. 核心代码示例
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.common.message.MessageExt;
import java.util.List;
public class PushConsumerDemo {
public static void main(String[] args) throws Exception {
// 1. 创建消费者实例
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("push_consumer_group");
consumer.setNamesrvAddr("127.0.0.1:9876");
// 2. 订阅Topic和Tag
consumer.subscribe("TestTopic", "*");
// 3. 注册消息监听器
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
ConsumeConcurrentlyContext context) {
// 4. 处理消息
for (MessageExt msg : msgs) {
System.out.println("收到消息: " + new String(msg.getBody()));
}
// 返回消费状态
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
// 5. 启动消费者
consumer.start();
System.out.println("消费者已启动");
}
}
2. Push模式关键点
-
长轮询机制:Broker在没有消息时会Hold住请求,新消息到达立即返回
-
负载均衡:消费者组内自动分配队列
-
流控参数:
// 设置每次拉取最大消息数(默认32)
consumer.setPullBatchSize(32);
// 设置每次消费最大消息数(默认1)
consumer.setConsumeMessageBatchMaxSize(10); -
消费模式:
集群模式(默认):组内消费者分摊消息
广播模式:每个消费者收到所有消息
四、两种模式对比
|-------|---------------|-------------|
| 特性 | Pull模式 | Push模式 |
| 实现复杂度 | 高,需自行管理offset | 低,自动管理 |
| 实时性 | 依赖拉取频率 | 高(长轮询) |
| 消费控制 | 完全自主控制 | 由RocketMQ控制 |
| 适用场景 | 特殊消费逻辑 | 常规消费场景 |
| 资源占用 | 低(按需拉取) | 中(长连接) |
五、最佳实践建议
-
常规场景:优先使用Push模式,简单高效
-
特殊需求:需要精确控制消费节奏时使用Pull模式
-
性能调优 :
1、调整
pullBatchSize
和consumeMessageBatchMaxSize
2、合理设置消费者线程数 -
容错处理 :
1、实现消息重试逻辑
2、处理消费超时情况
六、底层原理
RocketMQ的Push模式实际上是Pull模式的封装,其核心流程:
- RebalanceService:负责队列分配
- PullMessageService:后台线程执行拉取
- ProcessQueue:缓存拉取到的消息
- ConsumeMessageService:消费消息并回调监听器
这种设计既保持了Pull模式的灵活性,又提供了Push模式的易用性。