前提
-
之前学习了 Push 的方式,底层也是 Pull 模式,那么 Push 和 Pull 模式有什么区别吗。简单理解 Push 模式和 Pull 模式
- 推模式:消息发送者将消息发送到Broker,然后Broker主动推送给订阅了该消息的消费者。
- 拉模式:消息发送者将消息发送到Broker上,然后由消息消费者自发的向Broker拉取消息。
-
RocketMQ 中的退模式,严格意义上来讲,RocketMQ 并没有实现 PUSH 模式,而是对拉模式进行一层包装,在 RebalaceImpl 的时候产生拉取请求,在消费端开启一个线程 PullMessageService 循环向 Broker拉取消息,一次拉取任务结束后马上又发起另一次拉取操作,实现准实时自动拉取
在 22 年之前,拉取的处理类是 DefaultMQPullConsumer,现在改成了 DefaultMQLitePullConsumer,为啥要改呢,从名字上来看,应该比之前的额拉取服务更低的资源损耗,后续研究一波
源码
RocketMQ 拉模式,RocketMQ 消费者不自动向消息服务器拉取消息,而是将控制权移交给应用程序,RocketMQ消费者只是提供拉取消息API。怎么理解呢,就是最基本的拉取,你如果需要循环拉取,就给个循环,一切看用户使用方。
重要属性
java
public class DefaultLitePullConsumerImpl implements MQConsumerInner {
//负载均衡类
private RebalanceImpl rebalanceImpl = new RebalanceLitePullImpl(this);
}
回想一下 Push
- 当 Rebalace 检测到节点分布发生变化,就会窗机 PullRequest
- PullMessageServer 线程获取到 PullRequest 不断的去拉取消息,拉取完,继续放入队列中,达到不断拉取的效果
对于 Pull 这个平衡类,难道也向里面加入请求吗,如果加入就和 Push 没啥区别了,直接看里面的方法,从下面,可以看出,并不会将请求加入到队列中,那么也就不会和 Push 拉取消息,消费消息
scala
public class RebalanceLitePullImpl extends RebalanceImpl {
//为空
@Override
public void dispatchPullRequest(List<PullRequest> pullRequestList) {
}
}
重要方法
start
java
public synchronized void start() throws MQClientException {
switch (this.serviceState) {
case CREATE_JUST:
this.serviceState = ServiceState.START_FAILED;
this.checkConfig();
if (this.defaultLitePullConsumer.getMessageModel() == MessageModel.CLUSTERING) {
this.defaultLitePullConsumer.changeInstanceNameToPID();
}
//K1 将该消费者加入到 MQClientInstance 消费者列表中
initMQClientFactory();
//K1 填充 rebalanceImpl 对象的消费组、消息队列分配器、消费模式
initRebalanceImpl();
//K1 构建 PullAPIWrapper 对象,该对象封装了具体拉取消息的逻辑,PULL,PUSH 模式最终都会调用 PullAPIWrapper 类的方法从 Broker 拉取消息
initPullAPIWrapper();
//K1 根据集群消费模式(广播、集群)初始化消息进度管理器offsetStore
initOffsetStore();
mQClientFactory.start();
startScheduleTask();
this.serviceState = ServiceState.RUNNING;
log.info("the consumer [{}] start OK", this.defaultLitePullConsumer.getConsumerGroup());
operateAfterRunning();
break;
case RUNNING:
case START_FAILED:
case SHUTDOWN_ALREADY:
throw new MQClientException("The PullConsumer service state not OK, maybe started once, "
+ this.serviceState
+ FAQUrl.suggestTodo(FAQUrl.CLIENT_SERVICE_NOT_OK),
null);
default:
break;
}
}
-
问题
-
既然 Pull 模式无需自动拉取消息,但 PullMessageService 线程(消息拉取)+ RebalanceService 线程(消息队列负载)这个两个线程就没必要启动,这里启动了,会不会带来问题?
- 其实上面已经解答了,根本就不会加入 PullRequest,那么也就不会加入到 PullRequestQueue 中。而且从 PullMessageService 的代码也可以看出,所有的PullRequest 都是 PushConsumer 产生的
javaprivate void pullMessage(final PullRequest pullRequest) { //IMP 通过 PullRequest 获取消费者,但是对于 PullService 而言,不会创造 PullRequest,所以,此处所有的都是 PushConsumer final MQConsumerInner consumer = this.mQClientFactory.selectConsumer(pullRequest.getConsumerGroup()); if (consumer != null) { DefaultMQPushConsumerImpl impl = (DefaultMQPushConsumerImpl) consumer; impl.pullMessage(pullRequest); } else { log.warn("No matched consumer for the PullRequest {}, drop it", pullRequest); } }
-
-
ReblanceService 线程默认每 20s 进行一次消息队列重新负载,判断消息队列是否需要进行重新分布(如果消费者个数和主题的队列数没有发生改变),则继续保持原样。对于 PULL 模型,如果消费者需要监听某些主题队列发生事件,注册消息队列变更事件方法,则 RebalanceService 会将消息队列负载变化事件通知消费者。
-
至于 PULL 模式那些根据消息队列拉取消息的方法,与 PUSH 模式走的逻辑是一样的,唯一的区别是PULL 模式是需要应用程序收到触发消息拉取动作。
-
通过上述分析,我们总结一下RocketMQ,PUSH,PULL模式区别:
- PUSH: 消费者订阅主题,然后自动进行集群内消息队列的动态负载,自动拉取消息。准实时。
- PULL:消费者无需订阅主题,由业务方(应用程序)直接根据MessageQueue拉取消息。
项目中一般采用PUSH模式。
学习引入链接:blog.csdn.net/prestigedin...