消息消费模式(推 or 拉)
推(Push)模式
特点
- 由 Broker 主动将消息推送给消费者
- 实时性较高,消息到达后立即推送给消费者
- 消费者需要设置监听器(MessageListener)来处理推送过来的消息
实现原理
RocketMQ 的 Push 模式实际上是基于Pull模式的封装,内部实现了一个长轮询机制:
- 消费者启动后向 Broker 注册
- Broker 在有新消息时会主动推送给消费者
- 如果没有消息,Broker 会保持连接一段时间(默认15秒),期间如果有消息到达立即推送
- 如果超时仍无消息,返回空响应,客户端立即重新发起一个新的请求(又是15s)
优点
- 使用简单,开发者只需关注业务处理逻辑
- 实时性好,消息能快速被消费
- Broker 自动管理 offset,简化开发
缺点
- 消费者消费速度可能跟不上推送速度,导致消息堆积
- 客户端需要处理好流控
代码示例
// 1. 创建Push消费者实例,指定消费者组名
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("push_consumer_group");
// 2. 指定NameServer地址
consumer.setNamesrvAddr("127.0.0.1:9876");
// 3. 订阅Topic和Tag(*表示所有Tag)
consumer.subscribe("TestTopic", "*");
// 4. 注册消息监听器
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
ConsumeConcurrentlyContext context) {
// 处理消息
for (MessageExt msg : msgs) {
System.out.printf("收到消息: %s, Body: %s %n",
msg.getMsgId(), new String(msg.getBody()));
}
// 返回消费状态(成功)
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
// 5. 启动消费者
consumer.start();
拉(Pull)模式
特点
- 由消费者主动从 Broker 拉取消息
- 消费者需要自己管理 offset
- 消费者可以控制拉取的节奏和批量大小
实现方式
消费者需要手动编写拉取逻辑:
- 指定要拉取的 Topic 和队列
- 记录并管理消费位移(offset)
- 控制拉取频率和批量大小
优点
- 消费速度完全由消费者控制
- 可以灵活控制拉取节奏,适应不同场景
- 可以精确控制消费进度
缺点
- 实现复杂度高,需要自己管理offset
- 实时性不如 Push 模式
- 如果拉取频率控制不好,可能增加系统负载
代码示例
import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
import org.apache.rocketmq.client.consumer.PullResult;
import org.apache.rocketmq.client.exception.MQClientException;
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 PullConsumerExample {
// 用于存储各队列的消费进度
private static final Map<MessageQueue, Long> offsetTable = new HashMap<>();
public static void main(String[] args) throws MQClientException {
// 1. 创建Pull消费者实例,指定消费者组名
DefaultMQPullConsumer consumer = new DefaultMQPullConsumer("pull_consumer_group");
// 2. 指定NameServer地址
consumer.setNamesrvAddr("127.0.0.1:9876");
// 3. 启动消费者
consumer.start();
// 4. 获取指定Topic的所有消息队列
Set<MessageQueue> mqs = consumer.fetchSubscribeMessageQueues("TestTopic");
// 5. 遍历队列并拉取消息
for (MessageQueue mq : mqs) {
System.out.printf("从队列 %s 拉取消息%n", mq);
SINGLE_MQ:
while (true) {
try {
// 6. 获取当前队列的消费进度
long offset = getMessageQueueOffset(mq);
// 7. 从Broker拉取消息
PullResult pullResult = consumer.pullBlockIfNotFound(
mq, // 消息队列
null, // 子表达式(Tag过滤)
offset, // 开始拉取的offset
32); // 每次拉取的最大消息数
// 8. 处理拉取结果
switch (pullResult.getPullStatus()) {
case FOUND: // 找到消息
for (MessageExt msg : pullResult.getMsgFoundList()) {
System.out.printf("拉取到消息: %s, Body: %s %n",
msg.getMsgId(), new String(msg.getBody()));
}
// 更新消费进度
putMessageQueueOffset(mq, pullResult.getNextBeginOffset());
break;
case NO_MATCHED_MSG: // 没有匹配的消息
break;
case NO_NEW_MSG: // 没有新消息
break SINGLE_MQ;
case OFFSET_ILLEGAL: // offset非法
break;
default:
break;
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 9. 关闭消费者
consumer.shutdown();
}
// 获取指定队列的消费进度
private static long getMessageQueueOffset(MessageQueue mq) {
Long offset = offsetTable.get(mq);
if (offset != null) {
return offset;
}
return 0; // 默认从0开始
}
// 更新指定队列的消费进度
private static void putMessageQueueOffset(MessageQueue mq, long offset) {
offsetTable.put(mq, offset);
}
}
对比总结
| 特性 | Push模式 | Pull模式 |
|---|---|---|
| 实时性 | 高 | 取决于拉取频率 |
| 复杂度 | 低(框架封装) | 高(需自己管理offset) |
| 流量控制 | 由框架实现 | 完全由消费者控制 |
| 适用场景 | 大部分常规场景 | 需要精细控制消费节奏的场景 |
| 消息堆积风险 | 可能(消费速度跟不上推送速度) | 较小(消费速度可控) |
注意事项
- Push模式:
- 更简单易用,适合大多数场景
- 内部实际是基于Pull模式的长轮询实现
- 需要处理好消息监听器的逻辑,避免消息堆积
- Pull模式:
- 需要自己管理offset(示例中使用内存存储,生产环境应持久化)
- 需要控制好拉取频率,避免频繁请求
- 在新版RocketMQ中,Pull模式API已被标记为@Deprecated,推荐使用SimpleConsumer
- 通用配置:
- 生产环境需要配置合理的NameServer地址
- 注意消费者组名的唯一性
- 根据业务需求设置合适的Topic和Tag
- 版本兼容性:
- 上述示例基于RocketMQ 4.x版本
- 如果是5.x版本,Pull模式推荐使用新的SimpleConsumer API

浙公网安备 33010602011771号