基础概念回顾
在深入消费流程之前,我们先回顾几个核心概念,这些是理解后续内容的基础。
核心概念
Topic(主题):消息的逻辑分类,生产者发送消息到Topic,消费者从Topic订阅消息。一个Topic可以有多个队列。
Queue(队列):Topic的物理分割单元,消息实际存储在Queue中。每个Queue只能被同一个Consumer Group中的一个消费者实例消费,这是RocketMQ保证消息有序性和负载均衡的基础。
Consumer Group(消费者组):相同逻辑的消费者实例集合。同一个Consumer Group中的消费者共同消费一个Topic的消息,每条消息只会被组内的一个消费者消费。
消息位点(Offset):标识消费进度的指针,记录了消费者在某个Queue中消费到的位置。
推模式 vs 拉模式
RocketMQ提供了两种消费模式,但底层实现都是基于拉模式:
Push模式(推模式)
- 表面上是服务端主动推送消息给消费者
- 实际是客户端主动拉取,RocketMQ客户端封装了拉取逻辑
- 使用长轮询机制,当没有新消息时,请求会在服务端挂起一段时间
- 适合实时性要求高的场景,使用简单
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(); consumer.setMessageListener(new MessageListenerConcurrently() { // 消息处理逻辑 });
Pull模式(拉模式)
- 消费者主动向服务端拉取消息
- 需要开发者自己控制拉取频率和消费逻辑
- 更灵活,可以根据业务需求控制消费速度
- 适合批处理或对消费速度有特殊要求的场景
集群消费 vs 广播消费
集群消费(Clustering)
- 默认消费模式,同一个Consumer Group中的消费者共同消费Topic的消息
- 每条消息只会被组内的一个消费者消费
- 消费进度存储在Broker端,支持消费者动态伸缩
- 适合负载分担的场景
广播消费(Broadcasting)
- 每个消费者都会收到Topic的所有消息
- 消费进度存储在消费者本地
- 不支持重试和死信队列
- 适合配置下发、缓存刷新等场景
// 设置为广播消费 consumer.setMessageModel(MessageModel.BROADCASTING);
理解了这些基础概念,我们就可以深入消费流程的技术细节了。
消费流程详细解析
整体消费流程图
ConsumerProcessQueueBrokerMessageListenerThreadPool1. 启动阶段2. 消息拉取阶段alt[有消息][无消息(长轮询)]loop[持续拉取]3. 消息处理阶段位点会跳过失败消息的offsetalt[消费成功][消费失败]4. 位点提交获取路由信息返回Topic队列信息Rebalance分配队列PullRequest(批量32条)返回消息列表存入ProcessQueue挂起15s等待超时返回空从ProcessQueue取消息提交消息到线程池调用MessageListener返回SUCCESS处理成功从ProcessQueue移除消息更新本地消费位点(最小offset-1)返回RECONSUME_LATER处理失败从ProcessQueue移除失败消息发送消息到重试队列更新本地消费位点(最小offset-1)定时提交消费位点(5s间隔)ConsumerProcessQueueBrokerMessageListenerThreadPool
启动阶段详解
1. 配置初始化
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer_group"); consumer.setConsumeThreadMin(20); // 最小消费线程 consumer.setConsumeThreadMax(64); // 最大消费线程 consumer.setPullBatchSize(32); // 每次拉取消息数量
2. 路由信息获取
- Consumer向NameServer请求Topic的路由信息
- 获取Topic下所有Queue的分布情况(在哪个Broker上)
- 建立与相关Broker的连接
3. Rebalance队列分配
- 根据Consumer Group中的消费者数量和Topic的Queue数量进行分配
- 支持多种分配策略:平均分配(默认)、环形分配、一致性哈希、指定分配
- 分配结果决定了当前Consumer负责消费哪些Queue
消息拉取阶段详解
ProcessQueue的作用
ProcessQueue是Consumer端的本地消息缓存队列,每个Queue对应一个ProcessQueue:
// ProcessQueue关键属性 TreeMap<Long, MessageExt> msgTreeMap; // 按offset排序存储消息 AtomicLong msgCount; // 当前缓存消息数量 AtomicLong msgSize; // 当前缓存消息总大小 ReadWriteLock lockTreeMap; // 读写锁保护
主要作用:
- 缓存拉取的消息:避免频繁网络请求,提高消费效率
- 控制拉取速度:当缓存达到阈值时暂停拉取,防止内存溢出
- 维护消费顺序:使用TreeMap按offset排序,保证顺序消费
- 跟踪消费进度:记录哪些消息已消费,哪些未消费
长轮询拉取机制
// 关键参数设置 consumer.setPullBatchSize(32); // 每次批量拉取32条消息 consumer.setPullThresholdForQueue(1000); // ProcessQueue最大缓存1000条消息 consumer.setPullThresholdSizeForQueue(100); // ProcessQueue最大缓存100MB
拉取过程:
- 检查ProcessQueue状态:
- 如果缓存消息数 > 1000条,暂停拉取
- 如果缓存大小 > 100MB,暂停拉取
- Consumer向Broker发送PullRequest,请求拉取32条消息
- Broker检查是否有消息:
- 有消息:立即返回消息列表(最多32条)
- 无消息:挂起请求15秒,期间有新消息立即返回
- Consumer收到消息后放入ProcessQueue的msgTreeMap中
- 立即发送下一个PullRequest,保持持续拉取
消息处理阶段详解
线程池处理机制
每次从ProcessQueue中取出消息提交给消费线程池处理:
// 并发消费配置 consumer.setConsumeMessageBatchMaxSize(1); // 每次给线程处理1条消息 consumer.setConsumeThreadMin(20); // 最小20个线程 consumer.setConsumeThreadMax(64); // 最大64个线程
消费处理流程:
- ConsumeMessageService从ProcessQueue获取消息(默认每次1条)
- 调用MessageListener.consumeMessage()处理业务逻辑
- 根据返回结果处理:
SUCCESS:从ProcessQueue中移除该消息,更新本地消费位点RECONSUME_LATER:消费失败处理
消费失败后ProcessQueue的处理:
当消息消费失败返回RECONSUME_LATER时:
-
发送重试消息:
// 将失败消息发送到重试Topic: %RETRY% + ConsumerGroup// 设置重试次数和延时级别retryMsg.setReconsumeTimes(msg.getReconsumeTimes() + 1);
ProcessQueue中的处理:
- 立即移除:失败的消息立即从ProcessQueue的msgTreeMap中移除
- 更新位点:消费位点 = ProcessQueue中最小offset - 1
- 继续消费:ProcessQueue中后续消息可以继续被消费
位点更新机制详解:
位点的计算原理:消费位点 = ProcessQueue中最小未消费消息的offset - 1
举例说明:
- ProcessQueue中有消息:offset 100, 101, 102, 103, 104
- 当前消费位点:99
- 消费完offset 100后,从ProcessQueue移除,位点更新为:min(101,102,103,104) - 1 = 100
- 如果offset 102消费失败发送重试队列:
- 从ProcessQueue移除offset 102
- ProcessQueue剩余:101, 103, 104
- 消费完offset 101后,位点更新为:min(103,104) -

最低0.47元/天 解锁文章
1768

被折叠的 条评论
为什么被折叠?



