【mq读书笔记】消息拉取长轮训机制(Broker端)
RocketMQ并没有真正实现推模式,而是消费者主动想消息服务器拉取消息,推模式是循环向消息服务端发送消息拉取请求。
如果消息消费者向RocketMQ发送消息拉取时,消息未到达消费队列:
如果不启用长轮询机制消息并未达到消费队列,则会在服务端等待shortPollingTimeMills时间后再去判断消息是否已到达消息队列。如果消息未到达则提示消息拉取客户端消息不存在;
如果开启长轮训模式,mq一方面会每5s轮询检查一次消息是否可达,同时一有新消息到达后立马通知挂起线程再次验证新消息是否是自己感兴趣的消息,如果是则从commitlog文件提起消息返回给消息拉取客户端,否则直到挂起超时,超时时间由消息拉取方式在消息拉取时封装在请求参数重。push模式默认15s,pull模式通过DefaultMQPullConsumer#setBrokerSuspendMaxTimeMillis设置。
MQ通过在Broker端配置longPollingEnable为true来开启长轮询模式。
消息拉取时“服务端”未找到消息处理逻辑如下:

如果brokerAllowSuspend为true,表示支持挂起,则将响应对象response设置为null,将不会立即想客户端卸乳响应,hasSuspendFlag参数在拉取消息时,候检的拉取标记,默认为true。
这里创建创建拉取任务PullRequest并提交到PullRequestHoldService线程中。
共2个线程共同来完成轮询:
PullRequestHoldService:每个5s重试一次。
DefaultMessageStore.ReputMessageService每处理一次重新拉取,sleep(100),继续下一次检查。
PullRequestHoldService#suspendPullRequest:
public void suspendPullRequest(final String topic, final int queueId, final PullRequest pullRequest) {
String key = this.buildKey(topic, queueId);
ManyPullRequest mpr = this.pullRequestTable.get(key);
if (null == mpr) {
mpr = new ManyPullRequest();
ManyPullRequest prev = this.pullRequestTable.putIfAbsent(key, mpr);
if (prev != null) {
mpr = prev;
}
}
mpr.addPullRequest(pullRequest);
}
ManyPullRequest对象内部持有一个PullRequest列表,表示同一“主题@队列”的累积拉取消息任务。
PullRequestHoldService#run
@Override
public void run() {
log.info("{} service started", this.getServiceName());
while (!this.isStopped()) {
try {
if (this.brokerController.getBrokerConfig().isLongPollingEnable()) {
this.waitForRunning(5 * 1000);
} else {
this.waitForRunning(this.brokerController.getBrokerConfig().getShortPollingTimeMills());
} long beginLockTimestamp = this.systemClock.now();
this.checkHoldRequest();
long costTime = this.systemClock.now() - beginLockTimestamp;
if (costTime > 5 * 1000) {
log.info("[NOTIFYME] check hold request cost {} ms.", costTime);
}
} catch (Throwable e) {
log.warn(this.getServiceName() + " service has exception. ", e);
}
} log.info("{} service end", this.getServiceName());
}
private void checkHoldRequest() {
for (String key : this.pullRequestTable.keySet()) {
String[] kArray = key.split(TOPIC_QUEUEID_SEPARATOR);
if (2 == kArray.length) {
String topic = kArray[0];
int queueId = Integer.parseInt(kArray[1]);
final long offset = this.brokerController.getMessageStore().getMaxOffsetInQueue(topic, queueId);
try {
this.notifyMessageArriving(topic, queueId, offset);
} catch (Throwable e) {
log.error("check hold request failed. topic={}, queueId={}", topic, queueId, e);
}
}
}
}
遍历拉取任务表,根据主题与队列获取消息消费队列最大偏移量,如果该偏移量大于待拉取偏移量,说明有新的消息到达,调用notifyMessageArriving触发消息拉取。
PullRequestHoldService#notifyMessageArriving(java.lang.String, int, long, java.lang.Long, long, byte[], java.util.Map<java.lang.String,java.lang.String>):



如果消息队列的最大偏移量大雨待拉取偏移量,且消息匹配则调用executeRequestWhenWakeup将消息返回给消息拉取客户端,否则等待下一次尝试。

如果挂起超时时间超时,则不继续等待将直接返回客户端消息未找到。
看下wakeup的逻辑:
PullMessageProcessor#executeRequestWhenWakeup:

这里有回到了长轮询的入口,其核心是 设置brokerAllowSuspend为false,表示不支持拉取线程挂起,即当根据偏移量无法获取消息时,将不挂起线程等待新消息到来,而是直接返回告诉客户端本次消息拉取未找到消息。
如果开启长轮询,PullRequestHoldService线程会每隔5s被唤醒去尝试检测是否有新消息的到来直到超时,如果被挂起,需要等待5s,消息拉取实时性比较差。为避免这种情况,RocketMQ引入另外一种机制:当消息到达时,唤醒挂起线程触发一次检查。
【mq读书笔记】消息拉取长轮训机制(Broker端)的更多相关文章
- 【mq读书笔记】消息拉取
疑问:PullRequest何时添加? PullMessageService提供延迟添加与立即添加2种方式 疑问:PullRequest是在什么时候创建的呢? 1.上上图中 PullRequest p ...
- 【mq读书笔记】顺序消息
注意异常情况导致整个消费无限重试 阻塞消费 mq支持局部消息顺序消费,可以确保同一个消息消费队列中的消息被顺序消费.看下针对顺序消息在整个消费过程中做的调整: 队列负载: DefaultMQPushC ...
- 【mq读书笔记】消息过滤机制
mq支持表达式过滤和类过滤两种模式,其中表达式又分为TAG和SQL92.类过滤模式允许提交一个过滤类到FilterServer,消息消费者从FilterServer拉取消息,消息经过FilterSer ...
- 【mq读书笔记】消息确认(失败消息,定时队列重新消费)
接上文的集群模式,监听器返回RECONSUME_LATER,需要将将这些消息发送给Broker延迟消息.如果发送ack消息失败,将延迟5s后提交线程池进行消费. 入口:ConsumeMessageCo ...
- 【mq读书笔记】消息消费过程(钩子 失败重试 消费偏移记录)
在https://www.cnblogs.com/lccsblog/p/12249265.html中,PullMessageService负责对消息队列进行消息拉取,从远端服务器拉取消息后将消息存入P ...
- 【mq读书笔记】消息到达唤醒挂起线程检查新消息
DefaultMessageStore#start 当新消息到达CommitLog是,ReputMessageService线程负责将消息转发给ConsumeQueue,IndexFile,如果Bro ...
- 【mq读书笔记】mq消息消费
消息消费以组的的模式开展: 一个消费组内可以包含多个消费者,每一个消费组可订阅多个主题: 消费组之间有集群模式与广播模式两种消费模式:集群模式-主题下的同一条消息只允许被其中一个消费者消费.广播模式- ...
- 【mq读书笔记】客户端处理消息(回调提交到异步业务线程池,pullRequest重新入队)
看一下客户端收到消息后的处理: MQClientAPIImpl#processPullResponse private PullResult processPullResponse( final Re ...
- RocketMQ中PullConsumer的消息拉取源码分析
在PullConsumer中,有关消息的拉取RocketMQ提供了很多API,但总的来说分为两种,同步消息拉取和异步消息拉取 同步消息拉取以同步方式拉取消息都是通过DefaultMQPullConsu ...
随机推荐
- STM32入门系列-启动文件介绍
在启动文件内部使用的都是汇编语言,这个文件的作用是负责执行微控制器从"复位"到"开始执行 main 函数"中间这段启动时间所必须进行的工作.它完成的具体工作有: ...
- Android 教你如何发现 APP 卡顿
最近部门打算优化下 APP 在低端机上的卡顿情况,既然想优化,就必须获取卡顿情况,那么如何获取卡顿情况就是本文目的. 一般主线程过多的 UI 绘制.大量的 IO 操作或是大量的计算操作占用 CPU,导 ...
- python机器学习实现线性回归
线性回归 关注公众号"轻松学编程"了解更多. [关键词]最小二乘法,线性 一.普通线性回归 1.原理 分类的目标变量是标称型数据,而回归将会对连续型的数据做出预测. 应当怎样从一大 ...
- python爬虫自定义header头部
一.Handler处理器 和 自定义Opener 关注公众号"轻松学编程"了解更多. opener是 urllib.OpenerDirector 的实例,我们之前一直都在使用的ur ...
- [NOIP 2016D2T2/Luogu P1600] 天天爱跑步 (LCA+差分)
待填坑 Code //Luogu P1600 天天爱跑步 //Apr,4th,2018 //树上差分+LCA #include<iostream> #include<cstdio&g ...
- How to refresh datasource args caller[X++]
To refresh datasource args caller, you must add override method close on form like source code belo ...
- 【QT】子类化QObject+moveToThread实现多线程
往期链接: <QThread源码浅析> <子类化QThread实现多线程> 从往期<QThread源码浅析>可知,在Qt4.4之前,run 是纯虚函数,必须子类化Q ...
- 二维码生成与windows系统IP查询功能
一个木函是一款强大的手机软件,里面囊括了很多小功能,每一个都基本可以堪称小程序.那么,这些小功能具体是怎么实现的呢?让我们来一起来探讨二维码生成.IP查询这两个功能吧! 一.二维码生成 首先,我们来看 ...
- ESP32的Linux开发环境搭建
1. 官网教程地址 https://docs.espressif.com/projects/esp-idf/zh_CN/v4.0.1/get-started/linux-setup.html 2.官网 ...
- cetos6.5 gcc4.8 安装
1.准备源 #安装仓库 wget http://people.centos.org/tru/devtools-2/devtools-2.repo mv devtools-2.repo /etc/yum ...