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模式的易用性。

相关推荐
设计师小聂!5 分钟前
Seata分布式事物案例及详解
java·spring·spring cloud
编程乐学(Arfan开发工程师)6 分钟前
16、最佳实践-SpringBoot应用如何编写
java·spring boot·后端
取个好名称21 分钟前
适合初学者的 Blender 第二部分
java·前端·blender
xuanjiong26 分钟前
苍穹外卖day1实战,Idea中Lombok编译时“找不到符号”,更改JDK版本最全流程,作者亲身尝试
java·ide·intellij-idea
不秃的开发媛33 分钟前
JFace中MVC的表的单元格编辑功能的实现
java·开发语言·mvc
好奇的菜鸟39 分钟前
MyBatis-Plus 中 QueryWrapper 的 Limit 实现
java·tomcat·mybatis
_Tenk_40 分钟前
Java SpringBoot 扣子CozeAI SseEmitter流式对话完整实战 打字机效果
java·spring boot·语言模型·ai编程·智能体·coze扣子
越来越无动于衷1 小时前
java错题
java·算法
l_tian_tian_1 小时前
SpringCloud——MybatisPlus
java·spring boot·mybatis
北漂老男孩1 小时前
Flink基于Yarn多种启动方式详解
java·大数据·flink