这节介绍Consumer接收消息的流程,分为Pull和Push模式。
1. 初始化
上一节讲Rebalance时提到,Consumer接受客户端有两种方式:
Broker发现客户端列表有变化,通知所有Consumer执行Rebalance
Consumer定时每20秒自动执行Rebalance
其中1.的通知到达Consumer后,会立即触发Rebalance,然后会重置2.的定时器等待时间。二者最后通知Consumer的方式为
Push模式:当有新的Queue分配给客户端时,会新包装一个PullRequest,用于后续自动拉取消息,具体到DefaultMQPushConsumerImpl的executePullRequestImmediately方法
Pull模式:回调DefaultMQPullConsumerImpl的MessageQueueListener有Queue发生改变
2. Push模式
executePullRequestImmediately的内容为:
public void executePullRequestImmediately(final PullRequest pullRequest) {
this.mQClientFactory.getPullMessageService().executePullRequestImmediately(pullRequest);
}
即将PullRequest对象传给了PullMessageService的executePullRequestImmediately方法:
public void executePullRequestImmediately(final PullRequest pullRequest) {
try {
this.pullRequestQueue.put(pullRequest);
} catch (InterruptedException e) {
log.error("executePullRequestImmediately pullRequestQueue.put", e);
}
}
PullMessageService的结构如下:
内部维护着一个LinkedBlockingQueue属性pullRequestQueue,用于存储待处理的PullRequest;还有一个ScheduledExecutorService,用于延期处理PullRequest。具体流程如下:
RebalanceImpl调用DefaultMQPushConsumerImpl的executePullRequestImmediately方法,传入PullRequest
DefaultMQPushConsumerImpl内部调用PullMessageService的executePullRequestImmediately方法,该方法会把传入的PullRequest对象放到LinkedBlockingQueue中进行存储,等待后续处理。
PullMessageService会循环从队列中出队一个PullRequest,并调用自身的pullMessage用于后续处理。该动作会从MQClientInstance中选择对应的客户端实例DefaultMQPushConsumerImpl,并委托给它的pullMessage方法。
DefaultMQPushConsumerImpl会先判断当前请求是否满足条件,如果不满足条件,会调用PullMessage的executePullRequestLater方法,将当前请求延后处理。如果满足条件,会封装一个PullCallback对象用于处理异步消息,并调用PullAPIWrapper异步请求Broker拉取消息。
从上面的过程可以看出,Push模式内部还是客户端主动去拉取的,即所谓的封装拉模式以实现推模式,简单示意图如下:
内部通过PullMessageService循环的从PullRequest对应MessageQueue中主动拉取数据。
2.1. DefaultMQPushConsumerImpl.pullMessage(PullRequest)
该方法用于完成从MessageQueue拉取消息的过程,主要过程如下:
判断该MessageQueue对应的PullRequest是否已经标记为drop,如果是则直接返回
进行一系列的检查,如果检查不通过,则等待一定时间后再放回PullMessageService的待处理队列中,主要是通过PullMessageService中的ScheduledExecutorService来做到延迟执行,涉及的情况包括:
如果客户端未准备就绪(DefaultMQPushCOnsumerImpl执行start后status为RUNNING),则延迟PULL_TIME_DELAY_MILLS_WHEN_EXCEPTION(3000)后再放回PullMessage的队列中
如果是暂停状态,则延迟PULL_TIME_DELAY_MILLS_WHEN_SUSPEND(1000)后再放回PullMessageService的等待队列中
如果缓存的消息数大于配置的拉取线程数阈值(默认1000),则等