引言
RocketMQ消费者PULL请求模式拉取消息,是通过消费者端与服务端Broker的多个线程进行配合,做到消息拉取的及时与减少拉取的Broker性能损耗(通过长连接)。
-
消费者处理发起拉取请求的线程PullMessageService
-
服务端Broker处理未拉取到消息的hold线程PullRequestHoldService
-
服务端Broker消息写入后的重放后的线程ReputMessageService
消费者端
在消费者启动的过程,在构建MQClientInstance实例的过程中,会创建一个消息拉取线程PullMessageService,并在此启动过程中,开启此线程。
线程中会创建一个大小为0的messageRequestQueue消息请求队列
什么时候会放入messageRequestQueue中呢?
-
消费者实例启动中,随着消费者执行负载均衡后,把绑定的消费队列构建成MessageRequest,首次放入到消息请求队列中;
-
执行一次消息请求拉取后:
-
拉取成功后,会立即再次把当前消息拉取请求MessageRequest放入到消息请求队列中;
-
拉取失败或触发流控或者设置的pullInterval不为0,则会通过定时调度在延迟一定时间后,把MessageRequest进行放入到消息请求队列中
// 消息请求队列
private final LinkedBlockingQueue<MessageRequest> messageRequestQueue = new LinkedBlockingQueue<>();
// 定时调度的线程池(主要是执行延迟时间后,把消息请求放入消息请求队列)
private final ScheduledExecutorService scheduledExecutorService = Executors
.newSingleThreadScheduledExecutor(new ThreadFactoryImpl("PullMessageServiceScheduledThread"));
消息拉取线程run方法
public void run() {
// stopped属性(用volatile关键字修饰)默认为false,shutdown时置为true
while (!this.isStopped()) {
try {
// 从消息请求队列中取消息请求
MessageRequest messageRequest = this.messageRequestQueue.take();
// POP消息请求模式
if (messageRequest.getMessageRequestMode() == MessageRequestMode.POP) {
this.popMessage((PopRequest) messageRequest);
} else {
// PULL消息请求模式
this.pullMessage((PullRequest) messageRequest);
}
} catch (InterruptedException ignored) {
} catch (Exception e) {
logger.error("Pull Message Service Run Method exception", e);
}
}
}
pullMessage方法
DefaultMQPushConsumerImpl#pullMessage方法执行逻辑:
1、前置校验
-
校验消息处理队列ProcessQueue是否被删除(比如当前消费队列在消费者负载均衡后,不在属于当前消息费实例后,会把这个属性置为删除),若是删除状态,则直接退出;
-
校验消费者实例的状态是否为运行中,若不是,则通过定时任务在3s后,再进行消息拉取;
-
校验消息者是否被暂停,若是,则通过定时任务在1s后,再进行消息拉取;
-
校验是否触发了流控
-
当前消息队列对应的消息处理队列中还有1000条,则通过定时任务在50ms后,再进行消息拉取;
-
当前消息队列对应的消息处理队列消息大小100M,则通过定时任务执行在50ms后,再进行消息拉取;
-
非顺序消费时,当前消息队列对应的消息处理队列消息最大跨度(offset差值)大于200,则通过定时任务在50ms后,再进行消息拉取;
2、 创建拉取消息后的回调处理PullCallback
3、 生成sysFalg标志位
4、构建一次拉取消息请求Broker(默认是异步,一次拉取32条)
-
若返回的response不为空,则回调PullCallback进行消费消息,
-
若为空,则则通过定时任务执行延迟3000s后,再进行消息拉取;
服务端Broker
PullMessageProcessor#processRequest
-
校验是否允许消息拉取(如Topic是否存在,订阅关系是否存在等)
-
构建一个CompletableFuture,调用PullMessageResultHandler#handle获取拉取的消息结果:
-
若拉取消息成功,则把消息体返回给消费者端
-
若拉取不到消息,则把消息拉取请求放入到PullRequestHoldService中的pullRequestTable缓存中。
-
PullRequestHoldService是一个处理PullRequest的hold线程,若Broker配置了支持longPollingEnable(默认开启),则会每隔5s处理一次拉取校验(主要是调用notifyMessageArriving,获取是否有新的消息写入),若未支持,则会每隔1s处理一次拉取校验;
-
ReputMessageService消息重放线程,则会每隔1ms处理commitLog中的消息后,也会调用notifyMessageArriving方法进行通知,处理hold的pullRequst,这样可以做到消息到达时的及时返回给消费者端进行消费。