复盘女朋友面试4个月的RocketMQ面试题

又来了,这是复盘第六波女朋友面试4个月的面试题,本次是关于RocketMQ的专题

在分布式系统中,消息队列是一个使用场景非常丰富的技术。我们通常会用来作为异步通信,系统解耦,海量请求或者数据的削峰填谷,也可能使用延迟消息或者顺序消息完成特殊场景的业务功能。

消息队列产品有Kafka,RocketMQ, rabbitmq, 是市面上使用比较多的一个中间件, 也是Java后端面试中命中率较高的专题。

还是老套路,高频的面试题已经标星,有面试需要的同学可以先点星星收藏起来

架构相关题目

  • 说说RocketMQ架构组成有哪些?

哈哈,这个题目是不是就是传说中的送分题?我们如果使用过RocketMQ一般都可以回答出这个答案。

RocketMQ由生产者,消费者,broker,nameserver四个组件组成。前面三者都会和nameserver通信,nameserver可以理解成注册中心,他会存储所有生产者,消费者,broker的信息。broker是支持高可用的,他支持集群和主从模式。我们脑海里如果记住有下面这幅图就能解答这个问题了。

rocketMQ官网也有这部分的这部分更加详细的介绍。认识RocketMQ

使用姿势

  • 在集群消费模式下,16个消费者,8个写队列,有什么问题?

回答这个问题,需要知道Rocketmq负载均衡机制,默认使用平均分配策略,并且一个消费者最小消费的单元是队列。所以如果消费者数量比队列数大的时候,大于队列的消费者是无法消费到消息的,下面是RocketMQ官网给出的图:

因此,我们一定要合理规划topic下的队列数量。

功能原理相关题目

  • 顺序消息原理

这个问题需要从生产者和消费者,broker三端来解答,因为顺序消息需要三端配合。

  1. 生产者

生产者发送消息时候需要根据自己的业务规则,实现MessageQueueSelector接口的select方法,计算当前消息路由到哪个队列去。

java 复制代码
//队列负载均衡
public interface MessageQueueSelector {
    MessageQueue select(final List<MessageQueue> mqs, final Message msg, final Object arg);
}
//发送消息
SendResult sendResult = producer.send(msg, new MessageQueueSelector() {
                    @Override
                    public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
                        //arg 业务id
                        Integer id = (Integer) arg;
                        //生成订单 , 支付订单 保持顺序
                        System.out.println("订单id:"+id);
                        int index = id % mqs.size();
                        return mqs.get(index);
                    }
                }, orderId);
  1. 消费者

消费端消费的时候,consumer首先会向broker申请加锁,发送topic,queueId,clientId给broker,broker只会让这个消费者拉取对应队列的消息。

java 复制代码
for (MessageQueue mq : mqs) {

if (this.isLocked(group, mq, clientId)) {
    lockedMqs.add(mq);
} else {
    notLockedMqs.add(mq);
}
}

真正消费的地方,consumer还会加锁。

先对MessageQueue加锁

java 复制代码
public Object fetchLockObject(final MessageQueue mq) {
        Object objLock = this.mqLockTable.get(mq);
        if (null == objLock) {
            objLock = new Object();
            Object prevLock = this.mqLockTable.putIfAbsent(mq, objLock);
            if (prevLock != null) {
                objLock = prevLock;
            }
        }

        return objLock;
    }

真正处理消息前对processQueue加锁,保证当前消息队列Queue只被1个线程处理

java 复制代码
try {
      //对处理队列加锁
      this.processQueue.getLockConsume().lock();
      //调用业务逻辑处理消息
      status = messageListener.consumeMessage(Collections.unmodifiableList(msgs), context);
  } catch (Throwable e) {
      hasException = true;
  } finally {
      //处理队列解锁
      this.processQueue.getLockConsume().unlock();
  }

顺序消息的实现是比较复杂的,因为涉及到锁和线程并发控制,源码需要多读几次才能理解。面试时我们要注重讲解核心要点。

  • 事务消息原理

生产者发送事务消息(这是半消息,无法被消费者消费的)之后,开始执行本地事务,根据本地事务执行情况,告诉broker本地事务结果,要么成功,要么失败,如果是成功,那么broker会将事务消息还原到真实的topic和队列,如果是失败,broker会将事务消息(半消息)删除。

这里如果生产者没有告诉broker本地事务的执行结果,broker有一个兜底的定时任务,broker启用一个线程,扫描事务消息topic里的队列里面的消息,判断是否需要检查事务状态(最大检查15次)。

面试的时候回答还是要简洁一些,首先要把主流程讲解出来,其实这个细节原理在RocketMQ的实现内部也是比较复杂的。

高级特性

  • RocketMQ为什么这么快?

这个问题好像似曾相识,因为很多时候面试官喜欢问一些归类总结性的问题,这个问题就是这样,可以判断候选者对技术有没有自己分析总结的能力,这种题目其实没有标准答案,需要了解技术实现的情况下,进行分析总结。

我们还是需要从生产者,消费者,broker来分析

首先是生产者,生产者发送消息支持同步,支持异步,还支持oneway,oneway效率最高,因为他不用等broker返回。

在broker端,消息由索引和消息内容数据两部分组成,消息内容先需要写到commitlog,先写到pagecache,再刷新到磁盘,由于commitlog是顺序写的,而刷盘支持异步刷,这样性能是极高的。

如果是消费者从broker拉取消息, 先查询索引数据consumerQueue,这些索引数据占用空间小,通过页缓存读取,并且本地有缓存机制,读取性能也非常高,读取文件使用到零拷贝mmap技术提高性能,而且消费者和broker保持长轮训机制,使新消息到达可以快速投递到消费端。

最后在消费者这边支持并发多线程消费,将topic和队列信息会进行本地缓存,同时和broker保持长链接,能够保证及时接收到最新的消息。

总结

RocketMQ专题的知识其实很多,底层涉及的一些技术,比如rpc通信,编码解码,零拷贝,刷盘机制,主从复制,负载均衡算法等是大部分中间件通用的一些技术点,如果问这个专题的面试题,有很多题可能被问到。因此我们需要掌握实现机制,才能从容面对面试官的灵魂发问。

如果大家对其他专题的面试题感兴趣,可以关注我的高频面试题专栏,里面已经有5个高频面试专题,后续会陆续更新成体系,成专题的面试题。

相关推荐
丘山子42 分钟前
一些鲜为人知的 IP 地址怪异写法
前端·后端·tcp/ip
CopyLower1 小时前
在 Spring Boot 中实现 WebSockets
spring boot·后端·iphone
天天扭码2 小时前
总所周知,JavaScript中有很多函数定义方式,如何“因地制宜”?(ˉ﹃ˉ)
前端·javascript·面试
.生产的驴2 小时前
SpringBoot 封装统一API返回格式对象 标准化开发 请求封装 统一格式处理
java·数据库·spring boot·后端·spring·eclipse·maven
景天科技苑2 小时前
【Rust】Rust中的枚举与模式匹配,原理解析与应用实战
开发语言·后端·rust·match·enum·枚举与模式匹配·rust枚举与模式匹配
追逐时光者3 小时前
MongoDB从入门到实战之Docker快速安装MongoDB
后端·mongodb
天天扭码3 小时前
深入讲解Javascript中的常用数组操作函数
前端·javascript·面试
方圆想当图灵3 小时前
深入理解 AOP:使用 AspectJ 实现对 Maven 依赖中 Jar 包类的织入
后端·maven
豌豆花下猫3 小时前
Python 潮流周刊#99:如何在生产环境中运行 Python?(摘要)
后端·python·ai
渭雨轻尘_学习计算机ing3 小时前
二叉树的最大宽度计算
算法·面试