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端)的更多相关文章

  1. 【mq读书笔记】消息拉取

    疑问:PullRequest何时添加? PullMessageService提供延迟添加与立即添加2种方式 疑问:PullRequest是在什么时候创建的呢? 1.上上图中 PullRequest p ...

  2. 【mq读书笔记】顺序消息

    注意异常情况导致整个消费无限重试 阻塞消费 mq支持局部消息顺序消费,可以确保同一个消息消费队列中的消息被顺序消费.看下针对顺序消息在整个消费过程中做的调整: 队列负载: DefaultMQPushC ...

  3. 【mq读书笔记】消息过滤机制

    mq支持表达式过滤和类过滤两种模式,其中表达式又分为TAG和SQL92.类过滤模式允许提交一个过滤类到FilterServer,消息消费者从FilterServer拉取消息,消息经过FilterSer ...

  4. 【mq读书笔记】消息确认(失败消息,定时队列重新消费)

    接上文的集群模式,监听器返回RECONSUME_LATER,需要将将这些消息发送给Broker延迟消息.如果发送ack消息失败,将延迟5s后提交线程池进行消费. 入口:ConsumeMessageCo ...

  5. 【mq读书笔记】消息消费过程(钩子 失败重试 消费偏移记录)

    在https://www.cnblogs.com/lccsblog/p/12249265.html中,PullMessageService负责对消息队列进行消息拉取,从远端服务器拉取消息后将消息存入P ...

  6. 【mq读书笔记】消息到达唤醒挂起线程检查新消息

    DefaultMessageStore#start 当新消息到达CommitLog是,ReputMessageService线程负责将消息转发给ConsumeQueue,IndexFile,如果Bro ...

  7. 【mq读书笔记】mq消息消费

    消息消费以组的的模式开展: 一个消费组内可以包含多个消费者,每一个消费组可订阅多个主题: 消费组之间有集群模式与广播模式两种消费模式:集群模式-主题下的同一条消息只允许被其中一个消费者消费.广播模式- ...

  8. 【mq读书笔记】客户端处理消息(回调提交到异步业务线程池,pullRequest重新入队)

    看一下客户端收到消息后的处理: MQClientAPIImpl#processPullResponse private PullResult processPullResponse( final Re ...

  9. RocketMQ中PullConsumer的消息拉取源码分析

    在PullConsumer中,有关消息的拉取RocketMQ提供了很多API,但总的来说分为两种,同步消息拉取和异步消息拉取 同步消息拉取以同步方式拉取消息都是通过DefaultMQPullConsu ...

随机推荐

  1. 走在深夜的小码农 Seventh Day

    Css3 Seventh Day writer:late at night codepeasant 学习大纲: 1. 定位(position) 介绍 1.1 为什么使用定位 我们先来看一个效果,同时思 ...

  2. DateTimeFormatter接替SimpleDateFormat

    java程序猿经常会碰到的一个问题就是日期格式化.当接收参数中有日期或时间,那么就需要与前端商量好其格式,这边我知道是2种:1.时间戳 2.字符串. 先说一下时间戳,这个形势的参数,Java只需new ...

  3. KVM简介,安装及常见使用详解

    KVM简介 KVM(名称来自英语:Kernel-basedVirtual Machine的缩写,即基于内核的虚拟机),是一种用于Linux内核中的虚拟化基础设施,可以将Linux内核转化为一个hype ...

  4. 公钥、私钥、SSL/TLS、会话密钥、DES

    一,公钥私钥 1,公钥和私钥成对出现 2,公开的密钥叫公钥,只有自己知道的叫私钥 3,用公钥加密的数据只有对应的私钥可以解密 4,用私钥加密的数据只有对应的公钥可以解密 5,如果可以用公钥解密,则必然 ...

  5. js——事件循环

    JS-事件循环 js运行的环境称之为宿主环境. 执行栈 :call stack ,一个数据结构,用于存放各种函数的执行环境,每一个函数执行之前他的相关信息会加入到执行栈中,函数调用之前,创建执行环境, ...

  6. Luogu P1856 [USACO5.5]矩形周长Picture

    线段树+扫描线 经典的扫描线问题 首先将一个矩形看作由竖着的两条边和横着的两条边构成 那分成两次考虑,一次考虑竖边,一次考虑横边 首先考虑横边 如图两个矩形,现将横边擦去,留下竖边,将平面划分成3个区 ...

  7. iNeuOS工业互联平台,WEB组态(iNeuView)增加工程视图导入、导出功能,及优化和修复,发布:v3.2.1版本

    目       录 1.      概述... 2 2.      平台演示... 2 3.      导出组态工程文件... 2 4.      导入组态工程文件... 3  1.   概述 iNe ...

  8. 初次使用flask

    以写的一个小的例子来记录第一次使用: from flask import Flask, render_template import json # 实例化,可视为固定格式 app = Flask(__ ...

  9. CSS浮动好文章

    http://www.cnblogs.com/iyangyuan/archive/2013/03/27/2983813.html 看完上面这篇文章,我哭了.写的真好,我这块更菜.

  10. W3C中不同标准的含义

    学习CSS/HTML的过程中,当出现释义冲突时,W3C(万维网联盟)官网所陈列的技术标准是最核心的判断参考.但是新手在查阅W3C标准索引页面时,会发现同一个属性或者模型会出现多个不同的阶段规范展示结果 ...