RocketMQ学习十五-消费流控及进度提交

柯里星轨
• 阅读 2054

DefaultMQPushConsumer 的流量控制

PullMessageService根据偏移量从broker拉取一批消息后会首先放入 ProccessQueue处理队列,然后将这些消息消费任务提交到线程池之后返回。
ProcessQueue对象里主要的内容是一个 TreeMap 和一个读写锁。 TreeMap 里以 Message Queue 的 Offset作为 Key,以消息内容的引用为 Value,保存了 所有从 MessageQueue 获取到,但是还未被处理的消息; 读写锁控制着多个线程对 TreeMap 对象的并发访问。
有了ProcessQueue 对象,流量控制就方便和灵活多了,客户端在每次 Pull请求前会做几个判断,分别取但还未处理的消息个数、消息总大小、Offset的跨度,任何一个值超过设定的大小就隔一段时间再拉取消息,从而达到流量控制的目的。此外 ProcessQueue 还可以辅助实现顺序消费的逻辑。
代码实现在DefaultMQPushConsumerImpl#pullMessage方法里。将消息任务提交到线程池是在这个方法里的PullCallback回调里进行的,其中有ConsumeMessageConcurrentlyService,ConsumeMessageOrderlyService这两个实现,ConsumeRequest就是封装后的任务。

DefaultMQPushConsumerImpl.this.consumeMessageService.submitConsumeRequest

消息拉取方式

RocketMQ学习十五-消费流控及进度提交

  1. 经过队列负载机制后,会分配给当前消费者一些队列,注意一个消费组可以订阅多个主题,正如上面 pullRequestQueue 中所示,topic_test、topic_test2 这两个主题都分配了一个队列。
  2. 轮流从 pullRequestQueue 中取出一个 PullRequest 对象,根据该对象中的拉取偏移量向 Broker 发起拉取请求,默认拉取 32 条,可通过 pullBatchSize 参数进行改变,该方法不仅会返回消息列表,还会返更改 PullRequest 对象中的下一次拉取的偏移量。
  3. 接收到 Broker 返回的消息后,会首先放入 ProccessQueue(处理队列),该队列的内部结构为 TreeMap,key 存放的是消息在消息消费队列(consumequeue)中的偏移量,而 value 为具体的消息对象。
  4. 然后将拉取到的消息提交到消费组内部的线程池,并立即返回,并将 PullRequest 对象放入到 pullRequestQueue 中,然后取出下一个 PullRequest 对象继续重复消息拉取的流程,从这里可以看出,消息拉取与消息消费是不同的线程。
  5. 消息消费组线程池处理完一条消息后,会将消息从 ProccessQueue 中删除,然后会向 Broker 汇报消息消费进度,以便下次重启时能从上一次消费的位置开始消费。

消息消费进度提交

通过上面拉取消息流程我们知道,消息消费组线程池在处理完一条消息后,会将消息从 ProccessQueue 中移除,并向 Broker 汇报消息消费进度。
RocketMQ学习十五-消费流控及进度提交

那请大家思考一下下面这个问题:

例如现在处理队列中有 5 条消息,并且是线程池并发消费,那如果消息偏移量为 3 的消息(3:msg3)先于偏移量为 0、1、2 的消息处理完,那向 Broker 如何汇报消息消费进度呢?
如果提交 msg3 的偏移量为消息消费进度,那汇报完毕后如果消费者发生内存溢出等问题导致 JVM 异常退出,msg1 的消息还未处理,然后重启消费者,由于消息消费进度文件中存储的是 msg3 的消息偏移量,会继续从 msg3 开始消费,会造成消息丢失。
RocketMQ 采取的方式是处理完 msg3 之后,会将 msg3 从消息处理队列中移除,但在向 Broker 汇报消息消费进度时是取 ProceeQueue 中最小的偏移量为消息消费进度,即汇报的消息消费进度是 0。

RocketMQ学习十五-消费流控及进度提交

但如果出现上图这种情况,也就是0,1,3都已消费完且从ProcessQueue移除,那如果我们汇报的消费进度为2,如果发生内存溢出等异常情况,消费者重新启动,会继续从消息偏移量为 2 的消息开始消费,msg3 就会被消费多次(msg3会重新被broker里取出),故RocketMQ 不保证消息重复消费,所以消费的幂等是需要业务方进行保证的。

再看下提交消费进度的流程图:

RocketMQ学习十五-消费流控及进度提交

为了减少消费者与 Broker 的网络交互,提高性能,提交消息消费进度时会首先存入到本地缓存表中,然后定时上报到 Broker,同样 Broker 也会首先存储本地缓存表,然后定时刷写到磁盘。

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
美凌格栋栋酱 美凌格栋栋酱
9个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
4年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
4年前
List的Select 和Select().tolist()
List<PersondelpnewList<Person{newPerson{Id1,Name"小明1",Age11,Sign0},newPerson{Id2,Name"小明2",Age12,
Stella981 Stella981
4年前
Python之time模块的时间戳、时间字符串格式化与转换
Python处理时间和时间戳的内置模块就有time,和datetime两个,本文先说time模块。关于时间戳的几个概念时间戳,根据1970年1月1日00:00:00开始按秒计算的偏移量。时间元组(struct_time),包含9个元素。 time.struct_time(tm_y
Stella981 Stella981
4年前
RocketMQ消息轨迹
RocketMQ消息轨迹主要包含两篇文章:设计篇与源码分析篇,本节将详细介绍RocketMQ消息轨迹设计相关。RocketMQ消息轨迹,主要跟踪消息发送、消息消费的轨迹,即详细记录消息各个处理环节的日志,从设计上至少需要解决如下三个核心问题:消费轨迹数据格式记录消息轨迹(消息日志)消息轨迹数据存储在哪?1、消息轨迹数
Wesley13 Wesley13
4年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
4年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Stella981 Stella981
4年前
Noark入门之协议映射
0x00消息控制器消息控制器,主要作用就是为每个模块提供消息处理的入口.这里的消息不仅仅是协议,还有内部指令,事件等等逻辑入口,这也是为了响应线程模型作出的一种支撑,只要入口在此消息控制器内,那必然走期望的线程调度。@Controller用于标识一个类为当前模块的消息控制器入口.@Controller(threadGroup
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这