RocketMQ DefaultMQPushConsumer vs DefaultLitePullConsumer

1. 核心架构差异

DefaultMQPushConsumer (推送模式)

java 复制代码
// 服务端主动推送消息到消费者
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group");
consumer.subscribe("TopicTest", "*");
consumer.registerMessageListener(new MessageListenerConcurrently() {
    @Override
    public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
        ConsumeConcurrentlyContext context) {
        // 服务端推送消息到这里
        return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
    }
});

DefaultLitePullConsumer (拉取模式)

java 复制代码
// 客户端主动从服务端拉取消息
DefaultLitePullConsumer consumer = new DefaultLitePullConsumer("group");
consumer.subscribe("TopicTest", "*");
consumer.start();

// 手动控制拉取
List<MessageExt> messages = consumer.poll();
// 业务处理
consumer.commitSync(); // 手动提交

2. 消费重试机制对比

PushConsumer 重试机制

特点:支持重试,且是自动重试,支持到消息级别

java 复制代码
// 自动重试 - 消息级别
public class DefaultMQPushConsumer {
    // 最大重试次数,默认16次
    private int maxReconsumeTimes = 16;
    
    // 重试消息进入 %RETRY% 队列
    // 重试间隔:1s, 5s, 10s, 30s, 1m, 2m, 3m, 4m, 5m, 6m, 7m, 8m, 9m, 10m, 20m, 30m, 1h, 2h
}

// 消费失败处理
consumer.registerMessageListener(new MessageListenerConcurrently() {
    @Override
    public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
        ConsumeConcurrentlyContext context) {
        try {
            // 业务处理
            return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
        } catch (Exception e) {
            // 返回失败,自动进入重试队列
            return ConsumeConcurrentlyStatus.RECONSUME_LATER;
        }
    }
});

2.2 LitePullConsumer 重试机制

特点:没有内置的重试队列机制,不支持自动重试;要重试需要业务自己实现

java 复制代码
// 手动控制重试 - 需要业务代码实现
public class DefaultLitePullConsumer {
    // 没有内置的重试队列机制
    // 需要业务自己处理失败消息
}

// 手动重试示例
List<MessageExt> messages = consumer.poll(Duration.ofSeconds(10));
for (MessageExt message : messages) {
    try {
        processMessage(message);
        // 处理成功,记录偏移量
    } catch (Exception e) {
        // 处理失败,可以选择:
        // 1. 跳过此消息(可能丢失)
        // 2. 记录到死信队列手动处理
        // 3. 业务级重试逻辑
        log.error("Process message failed, msgId: {}", message.getMsgId(), e);
        
    }
}
// 批量提交,无法精确控制单条消息
consumer.commitSync();

3. 消费超时检查对比

PushConsumer 超时控制

特点:后台有消费超时检查,默认15min(从开始消费算起)。消费超时会将消息重发回broker(类似失败重试)。consumeTimeout可自行设置

java 复制代码
public class DefaultMQPushConsumer {
    // 消费超时时间,默认15分钟
    private long consumeTimeout = 15 * 60 * 1000;
    
    // 并发消费线程数控制
    private int consumeThreadMin = 20;
    private int consumeThreadMax = 64;
}

// 服务端会监控消费进度
// 如果消费线程卡住,Broker会重新投递消息

LitePullConsumer 超时控制

只有拉取超时,没有消费超时的检查

java 复制代码
public class DefaultLitePullConsumer {
    // 拉取超时控制
    private long pollTimeoutMillis = 10 * 1000;
    
    // 业务自己控制处理超时
    // 没有服务端超时检查
}

// 业务自己控制处理时间
List<MessageExt> messages = consumer.poll(Duration.ofSeconds(5));
long startTime = System.currentTimeMillis();
for (MessageExt message : messages) {
    // 业务需要自己控制单条消息处理超时
    if (System.currentTimeMillis() - startTime > 30000) {
        // 超时处理
        break;
    }
    processMessage(message);
}

4. 提交(Commit)机制对比

PushConsumer 提交机制

特点;仅可自动提交

java 复制代码
// 自动提交偏移量
consumer.registerMessageListener(new MessageListenerConcurrently() {
    @Override
    public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
        ConsumeConcurrentlyContext context) {
        // 返回 CONSUME_SUCCESS 后自动提交偏移量
        // 提交的是这批消息的最大偏移量
        return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
    }
});

// 异步提交,默认5秒提交一次
// 提交到 Broker 的消费进度管理

4.2 LitePullConsumer 提交机制

特点:默认自动提交,可手动提交

java 复制代码
// 手动提交,更灵活的控制
public class DefaultLitePullConsumer {
    // 提交方式
    public void commitSync();                    // 同步提交
    public void commitSync(Map<MessageQueue, Long> offsetTable); // 指定偏移量提交
    public void commitAsync();                   // 异步提交
    
}

// 使用示例
List<MessageExt> messages = consumer.poll();
if (!messages.isEmpty()) {
    // 处理消息
    boolean allSuccess = true;
    for (MessageExt message : messages) {
        if (!processMessage(message)) {
            allSuccess = false;
            break;
        }
    }
    
    if (allSuccess) {
        // 全部成功才提交
        consumer.commitSync();
    }
}

5. 使用场景推荐

DefaultMQPushConsumer 使用场景

适合场景
  1. 常规业务消息处理
  2. 需要严格不丢消息的场景
  3. 希望服务端管理消费状态,减少客户端复杂度
  4. 消费逻辑相对固定,不需要精细控制
优势
  • 自动重试机制完善
  • 服务端管理消费进度
  • 开发简单,专注业务逻辑
  • 消息可靠性高

DefaultLitePullConsumer 使用场景

适合场景
  1. 批处理任务
  2. 需要精细控制消费逻辑
  3. 消息处理时间不确定,需要自定义超时
  4. 需要实现复杂的分支逻辑
具体用例
java 复制代码
// 用例1:批处理控制
List<MessageExt> batch = consumer.poll();
if (batch.size() >= BATCH_SIZE) {
    processBatch(batch);
    consumer.commitSync();
}

// 用例2:条件消费
List<MessageExt> messages = consumer.poll();
for (MessageExt msg : messages) {
    if (meetsCondition(msg)) {
        processMessage(msg);
    }
    // 不满足条件的消息跳过,但偏移量会提交
    // 需要更精细的偏移量管理
}

// 用例3:自定义重试
Map<String, Integer> retryCountMap = new HashMap<>();
List<MessageExt> messages = consumer.poll();
for (MessageExt msg : messages) {
    String msgId = msg.getMsgId();
    int retryCount = retryCountMap.getOrDefault(msgId, 0);
    if (retryCount < MAX_RETRY) {
        if (processWithRetry(msg)) {
            retryCountMap.remove(msgId);
        } else {
            retryCountMap.put(msgId, retryCount + 1);
            // 不提交此消息偏移量
        }
    } else {
        // 超过重试次数,进入死信队列
        sendToDLQ(msg);
    }
}

6. 总结对比

选择建议

  • 大部分业务场景推荐使用 DefaultMQPushConsumer
  • 只有在需要精细控制消费逻辑时才使用 DefaultLitePullConsumer
  • 从 Push 切换到 Pull 需要慎重,因为失去了很多内置的可靠性保障
相关推荐
q***21602 小时前
【监控】spring actuator源码速读
java·spring boot·spring
Kuo-Teng2 小时前
LeetCode 142: Linked List Cycle II
java·算法·leetcode·链表·职场和发展
Moe4882 小时前
ConcurrentHashMap 重要方法实现原理和源码解析(一)
java·后端
拾忆,想起3 小时前
Dubbo核心架构全解析:构建微服务通信的高速公路
java·微服务·云原生·架构·dubbo·哈希算法
楠枬3 小时前
Spring Cloud 概述
java·spring cloud·微服务
♡喜欢做梦3 小时前
MyBatis操作数据库(入门)
java·数据库·mybatis
q***d1733 小时前
微服务与单体架构的成本对比
java·微服务·架构
冻感糕人~3 小时前
Agent框架协议“三部曲”:MCP、A2A与AG-UI的协同演进
java·人工智能·学习·语言模型·大模型·agent·大模型学习
阿Y加油吧3 小时前
java并发编程面试题精讲——day02
java·面试·c#