【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 ...
随机推荐
- Luogu P5072 [Ynoi2015]盼君勿忘
题意 给定一个长度为 \(n\) 的序列 \(a\) 和 \(m\) 次询问,第 \(i\) 次询问需要求出 \([l_i,r_i]\) 内所有子序列去重之后的和,对 \(p_i\) 取模. \(\t ...
- STM32入门系列-STM32时钟系统,时钟使能配置函数
之前的推文中说到,当使用一个外设时,必须先使能它的时钟.怎么通过库函数使能时钟呢?如需了解寄存器配置时钟,可以参考<STM32F10x中文参考手册>"复位和时钟控制(RCC)&q ...
- LoRa技术的特点和组成系统分析
目前,基于LoRa技术的网络层协议主要是LoRaWAN,也有少量的非LoRaWAN协议,但是通信系统网络都是星状网架构,以及在此基础上的简化和改进.主要包括以下3种. (1)点对点通信. 一点对一点通 ...
- Flink系列(0)——准备篇(流处理基础)
Apache Flink is a framework and distributed processing engine for stateful computations over unbound ...
- 【SpringBoot】10.SpringBoot文件上传
SpringBoot整合Thymeleaf 1.创建Thymeleaf的入门项目 maven构建简单项目 修改pom文件添加thymeleaf的坐标 <!-- thymeleaf的坐标 --&g ...
- 易于理解的 python 深度学习摘要算法教程
序 "我不想要一份完整的报告,只要给我一份结果摘要就好".我经常发现自己处于这种状况 -- 无论是在大学里还是在我的职业生涯中.我们准备一份全面的报告,但老师/主管却只有时间阅读摘 ...
- Spider--补充_None_global_urlparse
# 知识点补充: # 1) None: a = None if a: print("非None") else: print("None") if a is no ...
- psycopg2模块安装问题
我的平台是win10(x64).python3.7,打算通过psycopg2模块来操作Greenplum数据库,我通过pip install psycopg2 安装了psycopg2模块,也提示安装成 ...
- python pip install指定国内源镜像
有时候安装一些依赖包,网不好,直接超时,或者这个包就是死都下不下来的时候,可以指定国内源镜像. pip install -i 国内镜像地址 包名 e.g. pip install -i http:/ ...
- python之 《pandas》
pandas稍微比numpy处理数据起来还是要慢一点,pandas呢是numpy的升级版,可以说各有所长,numpy的优势是用来处理矩阵,而pandas的优势是处理数表. 1. Series 线性数表 ...