阿里二面:RocketMQ 消息积压了,增 加消费者有用吗?

RocketMQ(22) -- RocketMQ应用 -- 批量消费消息 RocketMQ应用 -- 批量消费消息 阅读详情

面试官:RocketMQ 消息积压了,增 加消费者有用吗?

我:这个要看具体的场景,不同的场景下情况是不一样的。

面试官:可以详细说一下吗?

我:如果消费者的数量小于 MessageQueue 的数量,增加消费者可以加快消 息消费速度,减少消 息积压。比如一个 Topic 有 4 个 MessageQueue,2 个消费者进行消费,如果增加一个消费者,明细可以加快拉取消息的频率。如下图:

image.png

如果消费者的数量大于等于 MessageQueue 的数量,增加消费者是没有用的。比如一个 Topic 有 4 个 MessageQueue,并且有 4 个消费者进行消费。如下图

image.png

面试官:你说的第一种情况,增加消费者一定能加快消 息 消 费的速度吗?

我:这…,一般情况下是可以的。

面试官:有特殊的情况吗?

我:当然有。消费者消息拉取的速度也取决于本地消息的消费速度,如果本地消息消费的慢,就会延迟一段时间后再去拉取。

面试官:在什么情况下消费者会延迟一段时间后再去拉取呢?

我:消费者拉取的消息存在 ProcessQueue,消费者是有流量控制的,如果出现下面三种情况,就不会主动去拉取:

  • ProcessQueue 保存的消息数量超过阈值(默认 1000,可以配置);
  • ProcessQueue 保存的消息大小超过阈值(默认 100M,可以配置);
  • 对于非顺序消费的场景,ProcessQueue 中保存的最后一条和第一条消息偏移量之差超过阈值(默认 2000,可以配置)。

这部分源码请参考类:org.apache.rocketmq.client.impl.consumer.DefaultMQPushConsumerImpl。

面试官:还有其他情况吗?

我:对于顺序消费的场景,ProcessQueue 加锁失败,也会延迟拉取,这个延迟时间是 3s。

面试官:消费者延迟拉取消息,一般可能是什么原因导致的呢?

我:其实延迟拉取的本质就是消费者消费慢,导致下次去拉取的时候 ProcessQueue 中积压的消息超过阈值。以下面这张架构图为例:

image.png

消费者消费慢,可 是能下面的原因:

  • 消费者处理的业务逻辑复杂,耗时很长;
  • 消费者有慢查询,或者数据库负载高导致响应慢;
  • 缓存等中间件响应慢,比如 Redis 响应慢;
  • 调用外部服务接口响应慢。

面试官:对于外部接口响应慢的情况,有什么应对措施吗?

我:这个要分情况讨论。

如果调用外部系统只是一个通知,或者调用外部接口的结果并 不处理,可以采用异步的方式,异步逻辑里采用重试的方式 保 证 接口调成 功。很多在问怎么进阶Java架构师或者是提升自己的Java技能,这边我推荐一个老师的 指导危号给你,你可以搜索找塔下,首先是:125 接着是:343 最后是是:1195 ,你连着数字就可以练习到了!有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

如果外部接口返回结果必须要处理,可以考虑接口返回的结果是否可以缓存默认值(要考虑业务可行),在调用失败后采用快速降级的方式,使用默认值替代返回接口返回值。

如果这个接口返回结果必须要处理,并且不能缓存,可以把拉取到的消息存入本地然后给 Broker 直接返回 CONSUME_SUCCESS。等外部系统恢复正常后再从本地取出来进行处理。

面试官:如果消 费 者数小于 MessageQueue 数量,并且外部系统响应正常,为了快速消费积压消息而增加消费者,有什么需要考虑的吗?

我:外部系统虽然响应正常,但是增加多个消费者后,外部系统的接口调用量会突增,如果达到吞吐量上限,外部系统会响应变慢,甚至被打挂。

同时也要考虑本地数据库、缓存的压力,如果数据库响应变慢,处理消息的速度就会变慢,起不到缓解消息积压的作用。

面试官:新增加了消费者后,怎么给它分配 MessageQueue 呢?

我:Consumer 在拉取消息之前,需要对 MessageQueue 进行负载操作。RocketMQ 使用一个定时器来完成负载操作,默认每间隔 20s 重新负载一次。

面试官:能详细说一下都有哪些负载策略吗?

我:RocketMQ 提供了 6 种负载策略,依次来看一下。

平均负载策略:

  1. 把消费者进行排序;
  2. 计算每个消费者可以平均分配的 MessageQueue 数量;
  3. 如果消费者数量大于 MessageQueue 数量,多出的消费者就分不到;
  4. 如果不可以平分,就使用 MessageQueue 总 数量对消费者数量求余数 mod;
  5. 对前 mod 数量消费者,每个消费者加一个,这样就获取到了每个消费者分配的 MessageQueue 数量。

比如 4 个 MessageQueue 和 3 个消费者的情况:

image.png

源代码的逻辑非常简单,如下:

// AllocateMessageQueueAveragely 这个类// 4 个 MessageQueue 和 3 个消费者的情况,假如第一个,index = 0int index = cidAll.indexOf(currentCID);// mod = 1int mod = mqAll.size() % cidAll.size();// averageSize = 2int averageSize =    mqAll.size() <= cidAll.size() ? 1 : (mod > 0 && index < mod ? mqAll.size() / cidAll.size()                                         + 1 : mqAll.size() / cidAll.size());// startIndex = 0int startIndex = (mod > 0 && index < mod) ? index * averageSize : index * averageSize + mod;// range = 2,所以第一个消费者分配到了2个int range = Math.min(averageSize, mqAll.size() - startIndex);for (int i = 0; i < range; i++) {    result.add(mqAll.get((startIndex + i) % mqAll.size()));}

循环分配策略:

这个很容易理解,遍 历 消费者,把 MessageQueue 分一个给遍历到的消费者,如果 MessageQueue 数量比消费者多,需要进行多次遍历,遍历次数等于 (MessageQueue 数量/消费者数量),还是以 4 个 MessageQueue 和 3 个消费者的情况,如下图:

image.png

源代码如下:

//AllocateMessageQueueAveragelyByCircle 这个类//4 个 MessageQueue 和 3 个消费者的情况,假如第一个,index = 0int index = cidAll.indexOf(currentCID);for (int i = index; i < mqAll.size(); i++) {    if (i % cidAll.size() == index) {        //i == 0 或者 i == 3 都会走到这里        result.add(mqAll.get(i));    }}

自定义分配策略:

这种策略在消费者启动的时候可以指定消费哪些 MessageQueue。可以参考下面代码:

AllocateMessageQueueByConfig allocateMessageQueueByConfig = new AllocateMessageQueueByConfig();//绑定消费 messageQueue1allocateMessageQueueByConfig.setMessageQueueList(Arrays.asList(new MessageQueue("messageQueue1","broker1",0)));consumer.setAllocateMessageQueueStrategy(allocateMessageQueueByConfig);consumer.start();

按照机房分配策略:

这种方式 Consumer 只消费指定机房的 MessageQueue,如下图:Consumer0、Consumer1、Consumer2 绑定 room1 和 room2 这两个机房,而 room3 这个机房没有消费者。

image.png

Consumer 启动的时候需要绑定机房名称。可以参考下面代码:

AllocateMessageQueueByMachineRoom allocateMessageQueueByMachineRoom = new AllocateMessageQueueByMachineRoom();//绑定消费 room1 和 room2 这两个机房allocateMessageQueueByMachineRoom.setConsumeridcs(new HashSet<>(Arrays.asList("room1","room2")));consumer.setAllocateMessageQueueStrategy(allocateMessageQueueByMachineRoom);consumer.start();

这种策略 broker 的命名必须按照格式:机房名@brokerName,因为消费者分配队列的时候,首先按照机房名称过滤出所有的 MessageQueue,然后再按照平均分配策略进行分配。

//AllocateMessageQueueByMachineRoom 这个类List<MessageQueue> premqAll = new ArrayList<MessageQueue>();for (MessageQueue mq : mqAll) {    String[] temp = mq.getBrokerName().split("@");    if (temp.length == 2 && consumeridcs.contains(temp[0])) {        premqAll.add(mq);    }}//上面按照机房名称过滤出所有的 MessageQueue 放入premqAll,后面就是平均分配策略

按照机房就近分配:

跟按照机房分配原则相比,就近分配的好处是可以对没有消费者的机房进行分配。如下图,机房 3 的 MessageQueue 也分配到了消费者:

image.png

如果一个机房没有消费者,则会把这个机房的 MessageQueue 分配给集群中所有的消费者。

源码所在类:AllocateMachineRoomNearby。

一致性 Hash 算法策略:

把所有的消费者经过 Hash 计算分布到 Hash 环上,对所有的 MessageQueue 进行 Hash 计算,找到顺时针方向最近的消费者节点进行绑定。如下图:

image.png

源代码如下:

//所在类 AllocateMessageQueueConsistentHashCollection<ClientNode> cidNodes = new ArrayList<ClientNode>();for (String cid : cidAll) {    cidNodes.add(new ClientNode(cid));}//使用消费者构建 Hash 环,把消费者分布在 Hash 环节点上final ConsistentHashRouter<ClientNode> router; //for building hash ringif (customHashFunction != null) {    router = new ConsistentHashRouter<ClientNode>(cidNodes, virtualNodeCnt, customHashFunction);} else {    router = new ConsistentHashRouter<ClientNode>(cidNodes, virtualNodeCnt);}//对 MessageQueue 做 Hash 运算,找到环上距离最近的消费者List<MessageQueue> results = new ArrayList<MessageQueue>();for (MessageQueue mq : mqAll) {    ClientNode clientNode = router.routeNode(mq.toString());    if (clientNode != null && currentCID.equals(clientNode.getKey())) {        results.add(mq);    }}

面试官:恭喜你,通过了。

RocketMQ入坑指南(四):消费者的使用方式 在这里先祝福大家新年快乐!因为假期原因,距离上一篇的生产者教程已经过了很久,今天是上班的第一天,我们继续分享RocketMQ的第四篇教程,主要介绍消费者相关的知识。消费者组:一个逻辑概念,在使用消费者时需要指定一个组名。一个消费者组可以订阅多个Topic。消费者实例:一个消费者组程序部署了多个进程,每个进程都可以称为一个消费者实例。订阅关系:一个消费者组订阅一个Topic的某一个 Tag,这种记录被称为订阅关系。实际使用中,要保证消费订阅关系一致,一个消费者组中的订阅Topic和Tag必须一致。 阅读详情

相关推荐

RocketMQ 一个topic 多个消费者只有一个消费的问题

前言 很多时候,我们会在多个地方同时订阅一个 topic,但是发现消费者只能执行一个 后注册消费者会顶替之前注册的消费者 原因 在 subscribeTable和 subscriptionInner方法中,是使用 Map 集合的方式存储topic订阅者 存储格式 大概为 {group : topic} Map 的特性就是 Key 不能重复,所以相同的 Key 会直接替换 解决方法 更改 subscribeTable和 subscriptionInner方法存储的 Key 也就是创建多个消费组,同

索性流年的博客 1万+

RocketMQ的Topic配置多消费方

同事压测过程中反馈MQ成为瓶颈,通过MQ控制台发现topic消费方只有一个,而这个RocketMQ是3台机器集群,故需要增加RocketMQ的consumer来缓解压力。broker_name是选择消费方数量,再写上topic名称,其他配置项和原来配置保持一样即可。3、点击对应topic的topic config,查看消费方有几个。4、若需要修改或新增topic的消费方,点击Add/Update。2、输入topic名称查询对应topic。1、登录到MQ控制台,点topic。

仰望的博客 5864

中科蓝讯AB536X/AB892X芯片开发避坑指南:Downloader配置中那些容易忽略的细节

本文详细解析中科蓝讯AB536X/AB892X芯片开发中Downloader配置的关键细节,包括软开关机实现、触摸灵敏度调试、量产校频等常见问题。特别针对无PR口芯片的替代方案、触摸参数优化、校频流程等易忽略环节提供实用解决方案,帮助开发者规避量产风险,提升开发效率。

weixin_30687051的博客 476

如何在 SpringBoot 项目中控制 RocketMQ消费线程数量

1 背景最近在新项目开发中遇到一个有趣的问题,如何在 SpringBoot 项目中控制 RocketMQ 消费线程数量。如何设置单个 topic 消费线程的最小数量和最大数量,用来区分不同 topic 吞吐量不同。我们先介绍一下 RocketMQ 消息监听再来说明 RocketMQ 消费线程。2 RocketMQ 消息监听设置消费者组为 my_consumer_g...

中生代技术 7420

LightGBM处理类别特征和缺失值有多省心?一个真实业务数据案例带你上手

本文通过真实电商数据案例,详细解析了LightGBM如何高效处理类别特征和缺失值。相比传统方法,LightGBM仅需一行代码即可自动处理高基数类别特征和缺失值,大幅提升模型训练效率和预测准确度,特别适合业务场景中的脏数据处理。

weixin_30689307的博客 460

关于Fluent模拟中的动态接触角UDF实现,详细设计

🏆本文收录于 《全栈Bug调优(实战版)》 专栏,该专栏专注于分享我在真实项目开发中遇到的各类疑难Bug及其深层成因,并系统提供高效、可复现的解决思路和实操方案。无论你是刚入行的新手开发者,还是拥有多年项目经验的资深工程师,本专栏都将为你提供一条系统化、高质量的问题排查与优化路径,助力你加速成长,攻克技术壁垒,迈向技术价值最大化与职业发展的更高峰🚀!

**My Coding Family** 1147

阿里二面:RocketMQ 消息积压了,增加消费者有用吗?

比如一个 Topic 有 4 个 MessageQueue,2 个消费者进行消费,如果增加一个消费者,明细可以加快拉取消息的频率。如果消费者的数量大于等于 MessageQueue 的数量,增加消费者是没有用的。这种策略 broker 的命名必须按照格式:机房名@brokerName,因为消费者分配队列的时候,首先按照机房名称过滤出所有的 MessageQueue,然后。:外部系统虽然响应正常,但是增加多个消费者后,外部系统的接口调用量会突增,如果达到吞吐量上限,外部系统会响应变慢,甚至被打挂。

mifffy_java的博客 977

NX二次开发 内部函数鼠标动态位置获取

内部函数鼠标动态位置获取

qq285503851的博客 3336

阿里二面:MQ 消息积压了,增加消费者有用吗?

Java学习指南:https://java-family.cn大家好,我是不才陈某~今天分享一道有意思的面试题。面试官:RocketMQ 消息积压了,增加消费者有用吗?我:这个要看具体的场景,不同的场景下情况是不一样的。面试官:可以详细说一下吗?我:如果消费者的数量小于 MessageQueue 的数量,增加消费者可以加快消息消费速度,减少消息积压。比如一个 Topic 有 4 个 Message...

码猿技术专栏 420

vscode配置matlab工作环境

前面有两篇文章我们分别讲了在vscode里面如何配置C语言环境和python环境,那两篇文章总体来说比较复杂,当然这篇也有许多有趣的问题。 附上链接: 文章目录matlab简介基本要求code runner插件与matlab相关的插件添加路径小结code runner是否支持更多语言更多选择另一个选择 matlab简介 MATLAB是美国MathWorks公司出品的商业数学软件,用于数据分析、无线通信、深度学习、图像处理与计算机视觉、信号处理、量化金融与风险管理、机器人,控制系统等领域。 MATLAB是ma

Cachel Wood的博客 4541

YOLOv12改进前必看 - YAML模型配置文件详细解读(再也不用担心通道数不匹配) 附网络结构图

YOLOv12的模型配置文件在中,里面包含yolov12的以及的不同大小的模型,本文将详细介绍一下YOLOv12模型文件中各参数的含义。

Limiiiing的博客 3276
上一篇: 太牛了!史上最全12W字Java面试题(含答案),面试之路畅通无阻
下一篇: 分享对于自学3DMAX来说,自学难吗?
java364103
博客等级 码龄5年 2粉丝 10原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值