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:

  1. public void suspendPullRequest(final String topic, final int queueId, final PullRequest pullRequest) {
  2. String key = this.buildKey(topic, queueId);
  3. ManyPullRequest mpr = this.pullRequestTable.get(key);
  4. if (null == mpr) {
  5. mpr = new ManyPullRequest();
  6. ManyPullRequest prev = this.pullRequestTable.putIfAbsent(key, mpr);
  7. if (prev != null) {
  8. mpr = prev;
  9. }
  10. }
  11.  
  12. mpr.addPullRequest(pullRequest);
  13. }

ManyPullRequest对象内部持有一个PullRequest列表,表示同一“主题@队列”的累积拉取消息任务。

PullRequestHoldService#run

  1. @Override
  2. public void run() {
  3. log.info("{} service started", this.getServiceName());
  4. while (!this.isStopped()) {
  5. try {
  6. if (this.brokerController.getBrokerConfig().isLongPollingEnable()) {
  7. this.waitForRunning(5 * 1000);
  8. } else {
  9. this.waitForRunning(this.brokerController.getBrokerConfig().getShortPollingTimeMills());
  10. }
  11.  
  12. long beginLockTimestamp = this.systemClock.now();
  13. this.checkHoldRequest();
  14. long costTime = this.systemClock.now() - beginLockTimestamp;
  15. if (costTime > 5 * 1000) {
  16. log.info("[NOTIFYME] check hold request cost {} ms.", costTime);
  17. }
  18. } catch (Throwable e) {
  19. log.warn(this.getServiceName() + " service has exception. ", e);
  20. }
  21. }
  22.  
  23. log.info("{} service end", this.getServiceName());
  24. }
  1. private void checkHoldRequest() {
  2. for (String key : this.pullRequestTable.keySet()) {
  3. String[] kArray = key.split(TOPIC_QUEUEID_SEPARATOR);
  4. if (2 == kArray.length) {
  5. String topic = kArray[0];
  6. int queueId = Integer.parseInt(kArray[1]);
  7. final long offset = this.brokerController.getMessageStore().getMaxOffsetInQueue(topic, queueId);
  8. try {
  9. this.notifyMessageArriving(topic, queueId, offset);
  10. } catch (Throwable e) {
  11. log.error("check hold request failed. topic={}, queueId={}", topic, queueId, e);
  12. }
  13. }
  14. }
  15. }

遍历拉取任务表,根据主题与队列获取消息消费队列最大偏移量,如果该偏移量大于待拉取偏移量,说明有新的消息到达,调用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. python常用模块numpy解析(详解)

    numpy模块 关注公众号"轻松学编程"了解更多. 以下命令都是在浏览器中输入. cmd命令窗口输入:jupyter notebook 后打开浏览器输入网址http://local ...

  2. ValueError: Unknown label type: 'continuous'

    说明:SVM训练的标签列必须为整型数值,不能为float.y = np.array(y, dtype=int)或y.astype('int')

  3. 简单操作elasticsearch(es版本7.6)

    简单操作elasticsearch(es版本7.6) es 官方文档 https://www.elastic.co/guide/index.html 简单操作elasticsearch主要是指管理索引 ...

  4. SQL service 数据插入

    目的:实现对数据库XDSA中表S72.C72.SC72的数据插入 1.构建数据库 2.构建表 3.插入数据 插入数据语句: ① 命令: INSERT INTO TableNameVALUES('值', ...

  5. JVM 整体回顾(一)

    JAVA虚拟机整体的回顾,为提纲类型 JAVA虚拟机是装在操作系统之上的一个应用软件[平台性质],作用是:将class格式的字节码编译成可执行的机器码.从而,class格式和物理机无关.也就是所谓的j ...

  6. IT人必知,互联网主流商业模式

    最近关注互联网电商营销相关方面的知识,对商业化和流量变现有了一些认知. 熟悉的朋友知道,写文章是我学习的一种方式,输出倒逼输入,继而强化知识体系,所以也把这段时间的输入,自顶向下做一个系列的分享. 对 ...

  7. 【linux】-Makefile简要知识+一个通用Makefile

    目录 Makefile Makefile规则与示例 为什么需要Makefile Makefile样式 先介绍Makefile的两个函数 完善Makefile 通用Makefile的使用 通用的Make ...

  8. 水题挑战6: CF1444A DIvision

    A. Division time limit per test1 second memory limit per test512 megabytes inputstandard input outpu ...

  9. 安装使用Pycharm及Anaconda最全教程

    网上安装anaconda和pycharm的教程很多,然而很少有人能够很详细地讲解,特别是对于pycharm的虚拟环境相关的说明很少,我也是懵逼的用了两年多,经常发现之前pycharm安装的第三方库,明 ...

  10. VMware虚拟机 - 解决主机每次重启后 VMWare 都无法上网的问题

    背景 每次重启自己电脑,重新打开 Vmware 虚拟机,再 ping 百度时,又显示无法 ping 通 但明明已经把 Vmware 的虚拟网卡重置了 那到底要如何解决呢? 解决方案 进入服务界面 将 ...