RocketMQ消息拉取模式详解

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模式关键点

  • 位点管理 :需要自行调用fetchConsumeOffsetupdateConsumeOffset管理消费进度
  • 队列遍历:需要获取Topic下所有队列并逐个处理
  • 状态处理 :需要处理FOUNDNO_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、调整pullBatchSizeconsumeMessageBatchMaxSize
    2、合理设置消费者线程数

  • 容错处理

    1、实现消息重试逻辑
    2、处理消费超时情况

六、底层原理

RocketMQ的Push模式实际上是Pull模式的封装,其核心流程:

  • RebalanceService:负责队列分配
  • PullMessageService:后台线程执行拉取
  • ProcessQueue:缓存拉取到的消息
  • ConsumeMessageService:消费消息并回调监听器

这种设计既保持了Pull模式的灵活性,又提供了Push模式的易用性。

相关推荐
极创信息9 小时前
信创系统认证服务怎么做?从适配到验收全流程指南
java·大数据·运维·tomcat·健康医疗
格鸰爱童话9 小时前
向AI学习项目技能(六)
java·人工智能·spring boot·python·学习
白宇横流学长10 小时前
停车场管理系统的设计与实现
java
Flittly10 小时前
【SpringAIAlibaba新手村系列】(18)Agent 智能体与今日菜单应用
java·spring boot·agent
木井巳10 小时前
【递归算法】目标和
java·算法·leetcode·决策树·深度优先
亦暖筑序10 小时前
手写 Spring AI Agent:让大模型自主规划任务,ReAct 模式全流程拆解
java·人工智能·spring
敖正炀10 小时前
ReentrantLock 与 synchronized对比
java
XiYang-DING10 小时前
【Java】二叉搜索树(BST)
java·开发语言·python
weixin_4379576110 小时前
Mysql安装不成功
java
Lyyaoo.10 小时前
【JAVA基础面经】进程安全问题(synchronized and volatile)
java·开发语言·jvm