RocketMQ 消费者没有服务端主动推送 ,本质是 PULL 拉模型;所谓 PushConsumer 只是 SDK 封装的伪推送,底层依然是循环向 Broker 发拉取请求。
两种模式
- PullConsumer:手动调用 pull (),自己控制什么时候去拉消息。
- DefaultMQPushConsumer(常用):底层自动 PULL 长轮询。
流程:
- 消费者发送
PullRequest请求 Broker:我要拉取某一个 MessageQueue 的消息。 - Broker 收到请求:
- 如果有消息:立刻把消息返回给消费者。
- 如果没有消息 :Broker不马上返回 response,把这个 pull 请求挂起,保存到等待队列。
- Broker 当收到新消息写入该队列时,唤醒挂起的 pull 请求,立刻返回消息给客户端。
- 如果一直没有消息,等待超时(默认 30s),返回空结果;消费者收到空,马上发起下一次 pull 请求。
长轮询机制
RocketMQ 底层都是通过 Netty 框架进行的通信(异步非阻塞 IO)。消费者向 Broker 发送 pull 请求后,业务线程不会阻塞,等 Broker 返回后通过回调函数处理结果,有消息则消费消息,无消息则重新发起 pull 请求。Broker 收到请求后也不会阻塞等待,先判断有无消息,若是有消息则立即返回,没有消息则将请求加入等待队列。默认等待 30s,期间有消息则唤醒请求返回消费者,没有消息则返回空结果。
这里有几个关键点值得展开:
- 消费者侧不阻塞:发送 pull 请求后,消费者线程可以继续做其他事情,等 Broker 响应回来再通过回调处理,因此单个消费者可以同时挂起多个 pull 请求,提升吞吐。
- Broker 侧也不阻塞:Broker 收到 pull 请求后,如果暂时没有消息,不会让线程干等,而是把请求挂到等待队列里,释放线程资源去处理其他请求。
- 等待队列的作用:挂起的请求会记录消费者要拉的 MessageQueue 和 offset,等新消息到达时,Broker 能精准找到对应的挂起请求并唤醒它。
- 超时兜底:如果 30s 内一直没有新消息,Broker 会主动返回空结果,避免请求无限期挂起;消费者收到空结果后立即发起下一次 pull,形成持续的轮询循环。
- 长轮询的价值:相比普通轮询(拉不到就立刻返回),长轮询让 Broker 帮忙"等一会儿",既减少了空轮询的次数,又能保证消息到达后尽快被消费,兼顾实时性和性能。
broker如何判断有无消息
Broker 处理 pull 请求时:
读取这个 queue 对应的 ConsumeQueue ,对比消费者提交的offset:
- 如果
offset < ConsumeQueue最大offset→ 有新消息,读取返回; - 如果 offset 已经追到末尾,没有新消息 → 将 pull 请求挂起,进入长轮询等待。
⚠️注意:判断有无消息看 ConsumeQueue 索引 ,不直接看 CommitLog。ReputMessageService 异步把消息构建进 ConsumeQueue 之后,pull 请求才看得到这条消息。
刚写入 CommitLog 但还没构建 ConsumeQueue 的消息,长轮询也看不到。