1、RocketMQ 推拉模式简介

消费者与消息存储方 Broker一般有两种通信机制:推(PUSH)、拉(PULL)。

推模式:消息发送者将消息发送到Broker,然后Broker主动推送给订阅了该消息的消费者。

拉模式:消息发送者将消息发送到Broker上,然后由消息消费者自发的向Broker拉取消息。

RocketMQ 推拉机制实现:严格意义上来讲,RocketMQ 并没有实现 PUSH 模式,而是对拉模式进行一层包装,在消费端开启一个线程 PullMessageService 循环向 Broker拉取消息,一次拉取任务结束后马上又发起另一次拉取操作,实现准实时自动拉取,PUSH 模式的实现请参考如下博文:

RocketMQ 拉模式,RocketMQ 消费者不自动向消息服务器拉取消息,而是将控制权移交给应用程序,RocketMQ消费者只是提供拉取消息API。

3、消息消费者启动流程分析

DefaultMQPullConsumerImpl#start

1 根据注册的主题,构建订阅信息,放入到RebalanceImpl的订阅表中

2 创建MQClientInstance,每一个clientConfig 一个 MqClientInstance 对象。

3 填充 rebalanceImpl 对象的消费组、消息队列分配器、消费模式。

4 构建 PullAPIWrapper 对象,该对象封装了具体拉取消息的逻辑,PULL,PUSH 模式最终都会调用 PullAPIWrapper 类的方法从 Broker 拉取消息。

5:根据集群消费模式(广播、集群)初始化消息进度管理器offsetStore。

6:将该消费者加入到 MQClientInstance 消费者列表中。

7:启动 MQClientInstance。该方法我们在讲解 DefaultMQPushConsumer 时详细讲解过

PullRequest 是由 RebalanceService 产生,它根据主题消息队列个数和当前消费组内消费者个数进行负载,然后产生对应的 PullRequest 对象,再将这些对象放入到 PullMessageService 的 pullRequestQueue队列。具体放入逻辑调用:RebalanceImpl#dispatchPullRequest(final List < PullRequest>pullRequestList);

至于 PULL 模式那些根据消息队列拉取消息的方法,与 PUSH 模式走的逻辑是一样的,唯一的区别是PULL 模式是需要应用程序收到触发消息拉取动作。

通过上述分析,我们总结一下RocketMQ,PUSH,PULL模式区别:

PUSH: 消费者订阅主题,然后自动进行集群内消息队列的动态负载,自动拉取消息。准实时。

PULL:消费者无需订阅主题,由业务方(应用程序)直接根据MessageQueue拉取消息。

项目中一般采用PUSH模式。

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

源码分析RocketMQ消息PULL-长轮询模式

1、长轮询、短轮询概述

消息拉取为了提高网络性能,在消息服务端根据拉取偏移量去物理文件查找消息时没有找到,并不立即返回消息未找到,而是会将该线程挂起一段时间,然后再次重试,直到重试。挂起分为长轮询或短轮询,在broker 端可以通过 longPollingEnable=true 来开启长轮询。

短轮询:longPollingEnable=false,第一次未拉取到消息后等待 shortPollingTimeMills时间后再试。shortPollingTimeMills默认为1S。

长轮询:longPollingEnable=true,会根据消费者端设置的挂起超时时间,受DefaultMQPullConsumer 的brokerSuspendMaxTimeMillis控制,默认20s,(brokerSuspendMaxTimeMillis),长轮询有两个线程来相互实现。

PullRequestHoldService:每隔5s重试一次。

DefaultMessageStore#ReputMessageService,每当有消息到达后,转发消息,然后调用PullRequestHoldService 线程中的拉取任务,尝试拉取,每处理一次,Thread.sleep(1), 继续下一次检查。

2、RocketMQ拉轮询拉取任务创建

org.apache.rocketmq.broker.processor.PullMessageProcessor#processRequest

在拉取消息时,如果拉取结果为 PULL_NOT_FOUND,在服务端默认会挂起线程,然后根据是否启用长轮询机制,延迟一定时间后再次重试根据偏移量查找消息。

如果不支持长轮询,则忽略 brokerSuspendMaxTimeMillis 属性,使用 shortPollingTimeMills,默认为1000ms作为下一次拉取消息的等待时间。

1:如果开启了长轮询模式,则每次只挂起 5s,然后就去尝试拉取。

2:如果不开启长轮询模式,则只挂起一次,挂起时间为 shortPollingTimeMills,然后去尝试查找消息。

3:遍历 pullRequestTable,如果拉取任务的待拉取偏移量小于当前队列的最大偏移量时执行拉取,否则如果没有超过最大等待时间则等待,否则返回未拉取到消息,返回给消息拉取客户端。具体请看

首先,要开启长轮询, 在 broker 配置文件中 longPollingEnable=true, 默认是开启的。

然后在 broker 端根据偏移量去消息存储文件中查找消息时,如果未找到,会挂起线程,然后轮询查找消息。所谓的轮询是轮询待拉取消息偏移大于消息消费队列的最大偏移量时才挂起,一旦检测发现待拉取消息偏移量小于消费队列最大偏移量时,则尝试拉取消息,结束长轮询过程。