一文整理常见Java后端面试题系列——RocketMQ篇(2022最新版)

关于作者
🐶 程序猿周周
⌨️ 短视频小厂BUG攻城狮
🤺 如果文章对你有帮助,记得关注、点赞、收藏,一键三连哦,你的支持将成为我最大的动力

本文是《后端面试小册子》系列的第 1️⃣6️⃣ 篇文章,该系列将整理和梳理笔者作为 Java 后端程序猿在日常工作以及面试中遇到的实际问题,通过这些问题的系统学习,也帮助笔者顺利拿到阿里、字节、华为、快手等多个大厂 Offer,也祝愿大家能够早日斩获自己心仪的 Offer。


PS:《后端面试小册子》已整理成册,目前共十三章节,总计约二十万字,欢迎👏🏻关注公众号【程序猿周周】获取电子版和更多学习资料(最新系列文章也会在此陆续更新)。公众号后台可以回复关键词「电⼦书」可获得这份面试小册子。文中所有内容都会在 Github 开源,项目地址 csnotes,如文中存在错误,欢迎指出。如果觉得文章还对你有所帮助,赶紧点个免费的 star 支持一下吧!

在这里插入图片描述

标题地址
MySQL数据库面试题总结(2022版)https://blog.csdn.net/adminpd/article/details/122910606
Redis面试题总结(2022版)https://blog.csdn.net/adminpd/article/details/122934938
计算机网络面试题总结(2022版)https://blog.csdn.net/adminpd/article/details/122973684
操作系统面试题总结(2022版)https://blog.csdn.net/adminpd/article/details/122994599
Linux面试题总结(2022版)https://blog.csdn.net/adminpd/article/details/122994862
Spring面试题总结(2022版)https://blog.csdn.net/adminpd/article/details/123016872
Java基础面试题总结(2022版)https://blog.csdn.net/adminpd/article/details/123080189
Java集合面试题总结(2022版)https://blog.csdn.net/adminpd/article/details/123171501
Java并发面试题总结(2022版)https://blog.csdn.net/adminpd/article/details/123266624
Java虚拟机面试题总结(2022版)https://blog.csdn.net/adminpd/article/details/123412605
Java异常面试题总结(2022版)https://blog.csdn.net/adminpd/article/details/123462676
设计模式面试题总结(2022版)https://blog.csdn.net/adminpd/article/details/123490442
Dubbo面试题总结(2022版)https://blog.csdn.net/adminpd/article/details/123538243
Netty面试题总结(2022版)https://blog.csdn.net/adminpd/article/details/123564362

1 基本概念

1、RocketMQ 是什么?

RocketMQ 是阿里巴巴在 2012 年开源的分布式消息中间件,目前已经捐赠给 Apache 软件基金会,并成为 Apache 的顶级项目。作为经历过多次阿里巴巴双十一这种超级工程的洗礼并有稳定出色表现的国产中间件,具有高性能、低延时和高可靠等特性。主要用来提升性能、系统解耦、流量肖峰等。

2、RocketMQ 有和特点?

1)灵活可扩展性

RocketMQ 天然支持集群,其核心四组件(Name Server、Broker、Producer、Consumer)每一个都可以在没有单点故障的情况下进行水平扩展。

2)海量消息堆积能力

采用零拷贝原理实现超大的消息的堆积能力,据说单机已可以支持亿级消息堆积,而且在堆积了这么多消息后依然保持写入低延迟。

3)支持顺序消息

可以保证消息消费者按照消息发送的顺序对消息进行消费。顺序消息分为全局有序和局部有序,一般推荐使用局部有序,即生产者通过将某一类消息按顺序发送至同一个队列来实现。

4)多种消息过滤方式

消息过滤分为在服务器端过滤和在消费端过滤。服务器端过滤时可以按照消息消费者的要求做过滤,优点是减少不必要消息传输,缺点是增加了消息服务器的负担,实现相对复杂。消费端过滤则完全由具体应用自定义实现,这种方式更加灵活,缺点是很多无用的消息会传输给消息消费者。

5)支持事务消息

RocketMQ 除了支持普通消息,顺序消息之外还支持事务消息,这个特性对于分布式事务来说提供了又一种解决思路。

6)回溯消费

回溯消费是指消费者已经消费成功的消息,由于业务上需求需要重新消费,RocketMQ 支持按照时间回溯消费,时间维度精确到毫秒,可以向前回溯,也可以向后回溯。

3、几种常见 MQ 的比较?

Kafka、ActiveMQ、RabbitMQ 以及 RocketMQ 各自的优缺点:

特性ActiveMQRabbitMQRocketMQKafka
单机吞吐量万级万级十万级十万级
Topic数量对吞吐量的影响topic 可以达到几百/几千的级别,吞吐量会有较小幅度的下降,这是 RocketMQ 的一大优势,在同等机器下,可以支撑大量的 topictopic 从几十到几百个时候,吞吐量会大幅度下降,在同等机器下,Kafka 尽量保证 topic 数量不要过多,如果要支撑大规模的 topic,需要增加更多的机器资源
时效性毫秒级微秒级,这是 RabbitMQ 的一大特点,延迟最低毫秒级毫秒级
可用性高,基于主从架构实现高可用非常高非常高
消息可靠性有较低的概率丢失数据基本不丢经过参数优化配置,可以做到 0 丢失同RocketMQ

一般的业务系统要引入 MQ,早起大家都用 ActiveMQ,但是现在已经使用的不多了,没经过大规模吞吐量场景的验证,社区也不是很活跃,所以大家还是算了吧,我个人不推荐用这个了;

后来大家开始用 RabbitMQ,但是确实 erlang 语言阻止了大量的 Java 工程师去深入研究和掌控它,对公司而言,几乎处于不可控的状态,但是确实人家是开源的,比较稳定的支持,活跃度也高;

不过现在确实越来越多的公司会去用 RocketMQ,确实很不错,毕竟是阿里出品,但社区可能有突然黄掉的风险(目前 RocketMQ 已捐给 Apache,但 GitHub 上的活跃度其实不算高)对自己公司技术实力有绝对自信的,推荐用 RocketMQ,否则回去老老实实用 RabbitMQ 吧,人家有活跃的开源社区,绝对不会黄。

所以中小型公司,技术实力较为一般,技术挑战不是特别高,用 RabbitMQ 是不错的选择;大型公司,基础架构研发实力较强,用 RocketMQ 是很好的选择。

如果是大数据领域的实时计算、日志采集等场景,用 Kafka 是业内标准的,绝对没问题,社区活跃度很高,绝对不会黄,何况几乎是全世界这个领域的事实性规范。

4、RocketMQ 的角色构成?
  • 生产者(Producer):负责产生消息,生产者向消息服务器发送由业务应用程序系统生成的消息。

  • 消费者(Consumer):负责消费消息,消费者从消息服务器拉取信息并将其输入用户应用程序。

  • 消息服务器(Broker):是消息存储中心,主要作用是接收来自 Producer 的消息并存储, Consumer 从这里取得消息。

  • 名称服务器(NameServer):用来保存 Broker 相关 Topic 等元信息并给 Producer ,提供 Consumer 查找 Broker 信息。

5、RocketMQ 执行流程?

1)启动 Namesrv 后开始监听端口,等待 Broker、Producer、Consumer 连上来,相当于一个路由控制中心。

2)Broker 启动,跟所有的 Namesrv 保持长连接,定时发送心跳包。

3)收发消息前,先创建 Topic。创建 Topic 时,需要指定该 Topic 要存储在哪些 Broker上,也可以在发送消息时自动创建 Topic。

4)Producer 向该 Topic 发送消息。

5)Consumer 消费该 Topic 的消息。

6、RocketMQ 的消息结构?
public class Message implements Serializable {
    // 表示消息要到的发送主题(必填)
    private String topic;
    // 消息的标记,完全由应用设置,RocketMQ不做任何处理
    private int flag;
    // 消息属性,主要存储一些消息的元数据信息
    private Map<String, String> properties;
    // 消息的内容,这是一个字节数组,序列化方式由应用决定
    private byte[] body;
    // 事务id,仅在事务消息中使用到 
    private String transactionId;
}
7、RocketMQ 为何这么快?

是因为使用了顺序存储、Page Cache 和异步刷盘。

1)在写入 commitLog 的时候是顺序写入的,这样比随机写入的性能有巨大提升。

2)写入 commitLog 的时候并不是直接写入磁盘,而是先写入操作系统的 PageCache。最后由操作系统异步将缓存中的数据刷到磁盘。

2、生产消费

1、消费者消费的几种模式?

RocketMQ 消费者有集群消费广播消费两种消费模式。

  • 集群消费

一个 Consumer Group 中的各个 Consumer 实例分摊去消费消息,即一条消息只会投递到一个 Consumer Group 下面的一个实例。

  • 广播消费

消息将对一个 Consumer Group 下的各个 Consumer 实例都投递一遍。即即使这些 Consumer 属于同一个Consumer Group ,消息也会被 Consumer Group 中的每个 Consumer 都消费一次。

2、消费者获取消息有几种模式?

消费者获取消息有两种模式:推送模拉取模式

  • PushConsumer

即推送模式,消息的能及时被消费。使用非常简单,内部已处理如线程池消费、流控、负载均衡、异常处理等等的各种场景。

但 RocketMQ 没有真正意义的 push,都是 pull,虽然有 push 类,但实际底层实现采用的是长轮询机制,即拉取方式。

  • PullConsumer

即拉取模式,应用主动控制拉取的时机,怎么拉取,如何消费等。主动权更高,但要自己处理各种场景。

3、如何获取 Topic-Broker 的映射关系?

Producer 和 Consumer 启动时,也都需要指定 Namesrv 的地址,从 Namesrv 集群中选一台建立长连接。

生产者每 30 秒从 Namesrv 获取 Topic 跟 Broker 的映射关系,更新到本地内存中。然后再跟 Topic 涉及的所有 Broker 建立长连接,每隔 30 秒发一次心跳。

3、Broker

1、Broker 中的消息被消费后会立即删除吗?

不会,每条消息都会持久化到 CommitLog 中,每个 Consumer 连接到 Broker 后会维持消费进度信息,当有消息消费后只是当前 Consumer 的消费进度(CommitLog的offset)更新了。

但 4.6 版本默认 48 小时后会删除不再使用的 CommitLog 文件。

2、Broker 如何保存消息数据?

RocketMQ 主要的存储文件包括 commitlog、consumequeue 以及 indexfile 三种文件。

Broker 在收到消息之后,会把消息保存到 commitlog 文件中,同时每个 Topic 对应的 messagequeue 下都会生成 consumequeue 文件用于保存 commitlog 的物理位置偏移量 offset,而 key 和 offset 的对应关系则使用 indexfile 保存。

CommitLog 文件保存于 ${Rocket_Home}/store/commitlog 目录中,从图中我们可以明显看出来文件名的偏移量,每个文件默认 1G,写满后自动生成一个新的文件。

由于同一个 Topic 的消息并不是连续的存储在 commitLog 中,消费者如果直接从 commitLog 获取消息效率非常低,所以通过 consumequeue 保存 commitLog 中消息的偏移量的物理地址,这样消费者在消费的时候先从 consumequeue 中根据偏移量定位到具体的 commitLog 物理文件,然后根据一定的规则(offset 和文件大小取模)在 commitLog 中快速定位。

3、为何使用 NameServer 而非 ZK?

NameServer 是专为 RocketMQ 设计的轻量级名称服务,为 producer 和 consumer 提供路由信息。具有简单、可集群横吐扩展、无状态,节点之间互不通信等特点。而 RocketMQ 的架构设计决定了只需要一个轻量级的元数据服务器就足够了,只需要保持最终一致,而不需要 Zookeeper 这样的强一致性解决方案,不需要再依赖另一个中间件,从而减少整体维护成本。

4、NameServer 如何保证最终一致性?

NameServer 作为一个名称服务,需要提供服务注册、服务剔除、服务发现这些基本功能,但是 NameServer 节点之间并不通信,在某个时刻各个节点数据可能不一致的情况下,下面分别从路由注册路由剔除以及路由发现三个角度进行介绍 NameServer 如何保证最终一致性。

1)路由注册:Broker 节点在启动时轮训 NameServer 列表,与每个 NameServer 节点建立长连接,发起注册请求。 注册后每 30s 向 NameServer 发送心跳包。

2)路由剔除:正常情况下 Broker 退出后会被 Netty 通道监听器监听到,异常情况下,NameServer 有一个定时任务,每隔 10s 扫描一下 Broker 表,剔除心跳包更新时间超过 120s 的 Broker。

3)路由发现:由于 NameServer 不会主动推送 Broker 信息,所以 RocketMQ 客户端提供了定时拉取 Topic 最新路由信息的机制(默认是30秒)。

4)由于路由信息是定时拉取得,所以需要加上(生产者)重试机制。

5、客户端 NameServer 选择策略?

客户端在获取路由信息时,每次都会尝试先从缓存的路由表中查找Topic路由信息,如果找不到,那么就去 NameServer 更新尝试。

具体选择哪个 NameServer 也是使用 round-robin 的策略。需要注意的是,尽管使用 round-robin 策略,但是在选择了一个 NameServer 节点之后,后面总是会优先选择这个节点,除非与这个节点通信出现异常的情况下,才会选择其它节点。

private final AtomicReference<List<String>> namesrvAddrList = new AtomicReference<List<String>>();

private final AtomicReference<List<String>> namesrvAddrList = new AtomicReference<List<String>>();

为了尽可能保证 NameServer 集群每个节点的负载均衡,在 round-robin 策略选择时,每个客户端的初始随机位置都不同。

6、RocketMQ 如何实现事务消息?

分布式系统中的事务可以使用 TCC(Try、Confirm、Cancel)、2pc 来解决分布式系统中的消息原子性。RocketMQ 4.3 中提供分布事务功能,通过 RocketMQ 事务消息能达到分布式事务的最终一致。

Half Message:预处理消息,当 Broker 收到此类消息后,会存储到 RMQ_SYS_TRANS_HALF_TOPIC 的消息消费队列中。

检查事务状态:Broker 会开启一个定时任务,消费 RMQ_SYS_TRANS_HALF_TOPIC 队列中的消息,每次执行任务会向消息发送者确认事务执行状态(提交、回滚、未知),如果是未知,Broker 会定时去回调在重新检查。

超时:如果超过回查次数,默认回滚消息。

事务消息实现也就是它并未真正进入 Topic 的队列中,而是用了临时队列来放所谓的half message,等提交事务后才会真正的将half message转移到 Topic 下的队列中。

7、事务消息的执行流程?

1)生产者先发送一条半事务消息到 Broker。

2)Broker 收到消息后返回 ACK 确认。

3)生产者开始执行本地事务。

4)如果事务执行成功发送 commit 到 Broke,失败发送 rollback。

5)如果 Broker 长时间未收到生产者的二次确认,则对生产者发起消息回查。

6)生产者查询事务执行最终状态。

7)根据查询事务状态再次提交二次确认。

最终,如果 Broker 收到二次 commit 确认,就可以把消息投递给消费者,反之如果是 rollback,消息会保存下来并且在 3 天后被删除。

8、RocketMQ 如何实现延迟消息?

原生 RocketMQ 延迟消息并不支持用户指定任意时间,而是通过设置延迟级别来指定的。RocketMQ 最多支持 18 个延迟级别,每个延迟级别对应的延迟时间可以通过配置 messageDelayLevel 自定义,默认值为 “1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h”。该配置属于 Broker,对所有 Topic 有效。

如果 Broker 接收到的是延迟消息,会改写消息 Topic 为 SCHEDULE_TOPIC_XXXX,queueId 为延迟级别 level-1,这样在构建 ConsumeQueue 的时候,消息就不会被构建到目标消费队列,消费者暂时也就无法消费这条消息了。然后由 ScheduleMessageService 服务,定时去扫描延迟队列里的消息,完成延迟消息的交付。

如果消息的交付时间到了,会构建新的 Message 对象恢复原来的 Topic 中,重新写回 CommitLog,之后 Consumer 就可以正常消费这条数据了。简单点说,就是 Broker 先替 Producer 将这条消息暂存到统一的延迟队列中,然后定时去扫描这个队列,将需要交付的消息重新写回到 CommitLog。

4、系统架构

1、RocketMQ 如何实现负载均衡?

RocketMQ 通过 Topic 在多 Broker 中分布式存储实现负载均衡,同时需要生产者、Broker 以及消费者多个不同角色共同完成。

  • Producer

发送端通过指定 queue 发送消息到相应的 Broker 中来达到写入时的负载均衡。

默认策略是随机选择,通过自增随机数对列表大小取余获取位置信息,自带容错策略。还可以通过 MessageQueueSelector 的 select 方法实现自定义。

  • Consumer

采用的是平均分配算法来进行负载均衡,支持一下几种负载均衡策略:

1)平均分配策略(AllocateMessageQueueAveragely),也是默认策略;
2)环形分配策略(AllocateMessageQueueAveragelyByCircle);
3)手动配置分配策略(AllocateMessageQueueByConfig);
4)机房分配策略(AllocateMessageQueueByMachineRoom);
5)一致性哈希分配策略(AllocateMessageQueueConsistentHash):
6)靠近机房策略(AllocateMachineRoomNearby)。

2、消息为何会重复消费?

影响消息正常发送和消费的重要原因是网络的不确定性

正常情况下在 Consumer 完成消费后应该发送 ACK,通知 Broker 该消息已正常消费,从 queue 中剔除。但当 ACK 因为网络原因导致丢失,Broker 会认为该消息没有被消费,此后会开启消息重投机制把消息再次投递到 Consumer。

3、RocketMQ 如何保证顺序消费?

首先多个 queue 只能保证单个 queue 里的顺序,queue 是典型的 FIFO ,天然支持顺序。多个 queue 同时消费是无法绝对保证消息的有序性的。

但可以通过同一个线程向一个 Topic 中的同一个队列发送消息,同时使用同一个线程消费该队列中的消息可以实现消息的顺序消费,这里的同一个线程最简单的实现就是单线程。

Producer 发送消息时重写 MessageQueueSelector 以指定发送同一个队列。

4、RocketMQ 如何保证消息不丢失?

首先我们要知道,RocketMQ 中间件的 Producer、Broker 以及 Consumer 三个组成部分都有肯能导致消息的丢失。

  • Producer 如何保证消息不丢失

1)采取 send() 同步发送消息,发送结果是同步感知的。

2)发送失败后可以重试,设置重试次数,默认 3 次。

producer.setRetryTimesWhenSendFailed(10);

3)集群部署,比如发送失败了的原因可能是当前 Broker 宕机了,重试的时候会发送到其它的 Broker 上。

  • Broker 如何保证消息不丢失

1)修改刷盘策略为同步刷盘,默认情况下是异步刷盘(即消息到内存后就返回确认信息)。

2)集群部署,默认异步复制到 slave,可以采用同步的复制方式,master 节点将会同步等待 slave 节点复制完成,才会返回确认响应。

  • Consumer 如何保证消息不丢失

消费过程需要注意返回消息状态,只有当业务逻辑真正执行成功后,才能返回 CONSUME_SUCCESS 的 ACK 确认。

5、RocketMQ 的消息堆积如何处理?

正常情况下我们增加 Consumer 即可:

  • 提高消费并行读

同一个 Consumer Group 下,通过增加 Consumer 实例的数量来提高并行度,但超过订阅队列数的 Consumer 实例无效。

提高单个 Consumer 的消费并行线程,通过 Consumer 的 consumerThreadMin 和 consumerThreadMax 来设置线程数。

  • 批量方式消费

通过设置 Consumer 的 consumerMessageBathMaxSize 这个参数,默认是 1,一次只消费一条消息,例如设置 N,那么每次消费的消息条数小于等于 N。

  • 丢弃非重要消息

  • 优化消息消费的过程

对于消费消息的过程一般包括业务处理以及跟数据库的交互,可以试着通过一些其他的方法优化消费的逻辑。

  • 临时解决方案

但如果 Consumer 和 Queue 不对等,上线了多台 Consumer 也无法在短时间内消费完堆积的消息时,处理起来相对麻烦:

1)准备一个临时的 Topic,队列数量是堆积的几倍;

2)上线一台 Consumer 做消息的搬运工,把原来 Topic 中的消息挪到新的 Topic 里,不做业务逻辑处理;

3)上线 N 台 Consumer 同时消费临时 Topic 中的数据;

4)修复 BUG,恢复原来的 Consumer,继续消费之前的 Topic。

6、如何实现分布式消息中间件?

可以从以下几个方面回答:

  • 需要考虑能快速扩容、天然支持集群。

  • 持久化的姿势。

  • 高可用性。

  • 数据零丢失的考虑。

  • 服务端部署简单、Client 端使用简单。

7、高吞吐量下如何优化生产者和消费者?

1)同一个 Group 下,多机部署,并行消费;

2)单个 Consumer 提高消费线程个数;

3)批量消费:批量拉取拉去消息以及业务逻辑的批量处理。

8、RocketMQ 如何保证数据的高容错性?

在不开启容错的情况下,轮询队列进行发送,如果失败了,重试的时候过滤失败的 Broker。

如果开启了容错策略,会通过 RocketMQ 的预测机制来预测一个 Broker 是否可用:

1)如果上次失败的 Broker 可用那么还是会选择该 Broker 的队列;

2)如果上述情况失败,则随机选择一个进行发送;

3)在发送消息的时候会记录一下调用的时间与是否报错,根据该时间去预测 Broker 的可用时间。

9、保证幂等的几种方法?

1)数据库唯一索引约束。

2)幂等时长,使用 Redis 存储消费记录。

3)Token 机制。

  • 5
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
回答: 要在Java中使用RocketMQ,你需要按照以下步骤进行操作: 1. 首先,你需要将RocketMQ解压到你的根目录。你可以使用以下命令解压:\[1\] ``` unzip rocketmq-all-4.5.2-bin-release.zip mv rocketmq-all-4.5.2-bin-release rocketmq ``` 2. 接下来,你需要编辑hosts文件以配置RocketMQ的名称服务器和代理。你可以使用以下命令编辑hosts文件:\[2\] ``` vim /etc/hosts ``` 3. 在hosts文件中,你需要添加名称服务器和代理的IP地址和主机名。例如: ``` nameserver 192.168.184.128 rocketmq-nameserver1 192.168.184.129 rocketmq-nameserver2 broker 192.168.184.128 rocketmq-master1 192.168.184.129 rocketmq-slave2 192.168.184.129 rocketmq-master2 192.168.184.128 rocketmq-slave1 ``` 4. 最后,你可以使用Java代码来接收RocketMQ的消息。你可以使用以下命令运行一个简单的消费者示例:\[3\] ``` tools.cmd org.apache.rocketmq.example.quickstart.Consumer ``` 这样,你就可以在Java中使用RocketMQ了。希望对你有所帮助! #### 引用[.reference_title] - *1* *2* [RocketMQ集群](https://blog.csdn.net/qq_45181415/article/details/115287595)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [一文搞懂Java整合RocketMQ](https://blog.csdn.net/Mr_YanMingXin/article/details/120302922)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿周周

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值