【面试】浅学RocketMQ

目录

什么是RocketMQ?

MQ有哪些使用场景

使用MQ的好处

MQ的优点

RocketMQ的组成部分

RocketMQ的架构

RocketMQ工作原理

什么是负载均衡

RocketMQ的数据是存储到内存还是硬盘,如果是硬盘那么怎么保证速度?

你们项目为什么要使用RocketMQ而不是使用kafka?

RocketMQ发送消息有几种方式

Queue的分配算法【消息队列的分配算法】

Consumer消息的拉取模式有哪两种

RocketMQ的消息消费模式

RocketMQ如何实现消息订阅和发布?

RocketMQ有哪几种部署类型?分别有什么特点?

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

RocketMQ宕机了怎么办【持久化机制】?

RocketMQ是如何保证消息顺序存储和顺序消费的?

RocketMQ延迟消息

如何设置固定时间执行一个方法?

RocketMQ事务消息的使用场景

RocketMQ事务消息工作原理

RocketMQ如何保证消息不丢失?

如何防止MQ消息重复消费



什么是RocketMQ?

        MQ全称为Message Queue,即消息队列 ,是一种提供消息队列服务的中间件,是一套提供了消息生产、存储、消费全过程的软件系统,遵循FIFO(先进先出)原则

RocketMQ有哪些使用场景

可以用来消峰,异步,解耦, 数据收集【收集日志】、大数据处理等、

  • 比如:双11等高并发场景下使用RocketMQ对请求进行排队,达到消峰的目的,防止大量请求打到服务器

  • 扣款成功通知】使用RocketMQ对服务之间进行解耦和,同时也达到异步的效果,提高系统的响应速度。

  • 还有就是对大数据量的处理【大量请求处理不完,存放在RocketMQ中】

异步/解耦: 如果是同步,会大大将低系统的吞吐量【单位时间内成功传送数据的数量】和并发度,且系统耦合度太高【如果一个部分出现故障,则会影响很多部分】 异步则会解决这个问题,在两个业务层之间添加一个RocketMQ层,即使支付系统挂掉也不会导致天府通的工作,只要把消息放在MQ消息队列即可,支付系统后续恢复再次执行RocketMQ中的消息即可。

消峰: MQ可以将系统的超量请求暂存RocketMQ消息队列中, 以便系统后期可以慢慢进行处理, 为了避免大量请求打到系统上,导致系统不稳定甚至挂掉。

数据收集

分布式系统会产生海量级数据流,如:业务日志、监控数据、用户行为等。针对这些数据流进行实时或 批量采集汇总,然后对这些数据流进行大数据分析,这是当前互联网平台的必备技术。通过RocketMQ完成此 类数据收集是最好的选择。

大数据处理

比如我们的平台向“三方平台”获取数据,一次请求了大量数据回来要进行处理,由于数据较多处理不过来,那么就可以放入MQ,再创建一些消费者进行数据处理即可。

使用MQ的好处

1提高系统响应速度【异步】

2提高系统稳定性【消峰】

3排序保证先进先出FIFO

RocketMQ的组成部分

Producer【生产消息】、Consumer【消费消息】、NameServer调度者【起到负载均衡,调度的作用】、Broker消息的存储、投递和查询。

【面试】浅学RocketMQ_第1张图片

Producer[生产者]

消息发布的角色,支持分布式集群方式部署。

Consumer[消费者]

消息消费的角色,支持分布式集群方式部署。支持以push推【Broker主动推送消息到ConsumerServer】,pull拉【ConsumerServe主动拉取Broker消息】两种模式对消息进行消费。

Broker[消息的存储]

Broker主要负责消息的存储、投递和查询

NameServer[起到负载均衡,调度的作用]

NameServer接受Broker集群的注册信息并且保存下来作为路由信息的基本数据。然后提供心跳检测机制,检查Broker是否还存活。

路由信息管理

每个NameServer将保存关于Broker集群的整个路由信息和用于客户端查询的队列信息。然后Producer和Conumser通过NameServer就可以知道整个Broker集群的路由信息,从而进行消息的投递和消费

RocketMQ的架构

RocketMQ主要包含四个角色Broker、NameServer、Consumer、Producer,为了增强Broker性能与吞吐量,Broker一般都是以集群形式出现的。各集群节点中可能存放着相同Topic的不同Queue。

不过,这里有个问题,如果某Broker节点宕机,如何保证数据不丢失呢?其解决方案是,将每个Broker集群节点进行横向扩展,即将Broker节点再建为一个HA集群,解决单点问题。

Broker节点【每一个服务器】集群是一个主从集群,即集群中具有Master与Slave两种角色。Master负责处理读写操作请求Slave负责对Master中的数据进行备份。当Master挂掉了,Slave则会自动切换为Master去工作。所以这个Broker集群是主备集群。Consumer既可以从Master订阅消息,也可以从Slave订阅消息

一个Master可以包含多个Slave,但一个Slave只能隶属于一个MasterMaster与Slave 的对应关系是通过指定相同的BrokerName、不同的BrokerId 来确定的。BrokerId为0表示Master非0表示Slave。每个Broker与NameServer集群中的所有节点建立长连接【client方与server先建立连接,连接建立后不断开,然后再进行报文发送和接收】,定时注册Topic信息到所有NameServer。

RocketMQ工作原理

  1. 启动NameServer,NameServer启动监听端口,等待Broker、Producer、Consumer进行连接, 相当于一个路由控制中心

  2. Broker启动,跟所有的NameServer保持长连接,定时发送心跳包。心跳包中包含当前Broker信息(IP+端口等)以及存储所有Topic信息。注册成功后,NameServer集群中就有Topic跟Broker的映射关系。

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

  4. Producer发送消息,启动时先跟NameServer集群中的其中一台建立长连接,获取到Broker信息,并创建Topic,将消息发送到Broker上进行存储。

  5. Consumer跟Producer类似,跟其中一台NameServer建立长连接【client方与server先建立连接,连接建立后不断开,然后再进行报文发送和接收】,然后直接跟Broker建立连接通道,开始消费消息。

【面试】浅学RocketMQ_第2张图片

什么是负载均衡

        在一台服务器的性能达到极限时,我们就需要使用服务器集群来提高系统的整体性能,在服务器集群中,需要一个服务器充当一个调度者【NameServer-也可以是一个集群】的作用,用户的所有请求都会都会由调度者【NameServer】进行接收,调度者根据每台服务器的负载情况将请求分配给某一台服务器进行处理。

调用者如何合理分配请求到服务器,保证服务器将性能充分发挥,从而保证服务器集群的性能最优,这就是负载均衡。

负载均衡算法:轮询,权重,随机,最小连接数,最少响应时间,哈希等

RocketMQ的数据是存储到内存还是硬盘,如果是硬盘那怎么保证速度?

RocketMQ 的数据是存储在硬盘上的,而不是存储在内存中。这种设计选择是为了确保数据的持久性和可靠性,即使在异常情况下(如服务器故障)也不会丢失消息。然而,为了保证速度和性能,RocketMQ 在数据存储方面采取了一些优化策略:

  1. Write-Ahead-Log(预写式日志): RocketMQ 使用 Write-Ahead-Log(WAL)技术。当消息到达 RocketMQ 时,首先将消息写入日志文件(CommitLog)中的预写式日志,然后再异步将消息写入具体的存储文件。这样可以减少硬盘的随机写入,提高写入性能。

  2. 内存映射(Memory-Mapped Files): RocketMQ 使用内存映射技术来加速消息的读写操作。通过内存映射,可以将存储文件的一部分映射到内存中,实现对存储文件的直接读写。这样可以避免频繁的磁盘 I/O 操作,提高读写性能。

  3. 顺序写入和读取: RocketMQ 的消息存储文件是按照顺序写入的,每个队列都有一个存储文件。这种设计保证了消息的连续性,可以减少磁盘寻址时间,提高读写性能。

  4. 批量写入和刷盘策略: RocketMQ 采用批量写入的方式,将多条消息一次性写入磁盘,减少了写入的系统调用开销。同时,RocketMQ 也提供了不同的刷盘策略,可以配置消息何时被刷写到磁盘,平衡了数据的可靠性和性能。

  5. 索引和缓存: RocketMQ 使用索引文件来加速消息的查找。索引文件存储了消息的偏移量和关键属性,用于快速定位消息。此外,RocketMQ 也会在内存中维护一些缓存,以加速消息的读取操作。

虽然 RocketMQ 的数据存储在硬盘上,但通过上述优化策略,RocketMQ 在保证数据持久性的同时,也能够在一定程度上提高读写性能。

你们项目为什么要使用RocketMQ而不是使用kafka?

主要是在选型的时候考虑到他们的区别,最主要的还是看业务的需求。

适用场景:【明显区别】

RocketMQ:主要用作业务处理

kafka:日志处理

性能(单机)

事务的整个流程:

1客户端向服务器发请求

2服务器进行处理请求

3将处理结果返回给客户端

RocketMQ:TPS 10w(每秒)【Transactions per Second】每秒事务数

kafka:TPS 100W

结论:kafka性能更高

可靠性

RocketMQ:支持同步刷盘,异步刷盘,同步复制,异步复制

kafka:支持异步刷盘,异步复制

结论:RocketMQ的可靠性好

  1. 同步刷盘(Sync Flush)

    • 在同步刷盘模式下,当消息写入内存后,会立即将数据刷写到持久化存储设备(如硬盘)中。在数据被确认持久化之前,写操作会一直阻塞,确保消息已经写入持久化存储再返回成功。
    • 优点:数据持久性较高,对于系统故障或崩溃,数据损失的风险较低。
    • 缺点:写入操作阻塞,可能影响系统的写入性能。
  2. 异步刷盘(Async Flush)

    • 在异步刷盘模式下,消息首先写入内存,然后系统会异步地将内存中的数据批量刷写到持久化存储中。写操作不会阻塞,允许更快的写入速度。
    • 优点:写入性能较高,不会阻塞写操作,适用于高吞吐量场景。
    • 缺点:在数据被刷写到磁盘之前,如果系统发生故障,可能会导致数据丢失。
  3. 同步复制

    • 在同步复制中,当数据写入主节点后,主节点会等待所有副本节点都确认收到数据并成功写入后,才返回写入成功的响应给客户端。只有在所有副本都写入成功后,写操作才被认为是完成的。
    • 优点:数据一致性较高,即使发生节点故障,也能够保证数据不会丢失。
    • 缺点:写操作的延迟较大,因为需要等待所有副本的确认。可能影响系统的写入性能。
  4. 异步复制

    • 在异步复制中,主节点将数据写入后,不等待副本节点的确认,直接返回写入成功的响应给客户端。副本节点会异步地将数据复制到它们的存储中。写操作返回后,不保证所有副本都已写入成功。
    • 优点:写操作的响应时间较低,不会受到副本节点的延迟影响。适用于高吞吐量的场景。
    • 缺点:数据一致性风险较高,如果主节点故障,未被复制的数据可能丢失。

顺序性【RocketMq顺序性好】

RocketMq:支持严格的顺序消息,在顺序消费的场景下,一台Broker【接收消息,存储,投递】宕机后,发送消息失败,不会出现乱序。

kafka:kafka在某些配置下,支持顺序消息,但在一台Broker宕机后消息会乱序

消息失败重试【明显区别】

RocketMQ:支持失败重试。

kafka:不支持

延时消息【明显区别】

RocketMQ:支持

kafka:不支持

分布式事务【明显区别】

RocketMQ:支持

kafka:不支持

消息回溯

RocketMQ:支持某个时间戳(毫秒级)来回溯消息;

kafka:支持某个偏移量offset来回溯消息;

各有千秋,不分伯仲。

RocketMQ发送消息有几种方式

RocketMQ发送消息支持:同步,异步,单向消息

  • 同步消息: 【必须等待结果才能执行下一步操作】发送者发送消息,需要等待结果的返回,才能继续发送第二条消息,这是一种阻塞式模型,虽然消息可靠性高,但是阻塞导致性能低

  • 异步消息:【不用等待结果,继续执行下一步操作】它是通过回调的方式来获取到消息的发送结果,消息可靠性高,性能也高

  • 单向消息:【只管发送,不管结果】 单向发送是没有返回结果值的,可靠性不高

使用场景建议如下

  • 如果是比较重要的不可丢失的消息,且对时效性要去不高建议使用同步发送[安全性好],如转账消息

  • 如果是不重要的可失败的消息,建议使用单向发送比如日志消息,

  • 如果对时效性要求比较高【可以接收丢失信息】,且消息能够接收丢失,可以尝试使用异步发送【地铁进出站,滴滴打车支付】

Queue的分配算法【消息队列的分配算法】

Queue是如何信息分配给Consumer的,这对应了四种算法:

平均分配策略,环形平均策略,一致性Hash策略,同机房策略。

  • 平均分配策略【默认】:根据 queueCount 【队列总数】/ consumerCount【消费者总数】 作为每个消费者平均分配数量,如果多出来的queue就再依次逐个分配给Consumer

  • 环形平均策略:根据消费者的顺序,将Queue中的消息一个一个的分配给Consumer。即可类似于发扑克牌

  • 一致性Hash策略 : 该算法将Consumer的Hash值作为节点放到Hash环上,然后将Queue的hash值也放入Hash环上,通过顺时针进行就近分配【分配效率较低,容易导致分配不均匀,适用于consumer经常变化的场景】。

  • 同机房策略该算法会根据queue部署的机房位置和consumer的位置,过滤出跟当前consumer相同机房的queue。然后按照平均分配策略或环形平均策略同机房queue进行分配【先消费相同机房的信息】。如果没有同机房queue,则按照平均分配策略或环形平均策略对所有queue进行分配。

平均分配性能比较高,一致性Hash性能不高,但是能减少消费者组进行扩容,减少Rebalance,如果Consumer数量变动频繁可以使用一致性Hash。

推荐查看:

RocketMQ(九)—Queue分配算法_稷下学员的博客-CSDN博客_rocketmq 队列分配

Consumer消息的拉取模式有哪两种

消息到消费分为:拉取式 pull 和推送是 push

  • Pull:拉取式Consumer去Broker进行拉取】,需要消费者间隔一定时间就去遍历关联的Queue,实时性差,可控性好。【能够降低服务器的负载压力】

  • Push:推送式【Broker push到Consumer】【Broker进行自动推送信息】封装了Queue的遍历,实时性强但会增加服务器的负载压力。】,但是对系统资源占用比较多

RocketMQ的消息消费模式

消息消费模式有两种:Clustering(集群消费)和Broadcasting(广播消费)

默认情况下就是集群消费,RocketMQ中的一条消息只能被集群内的一个消费者进行消费。如果某个消费者挂掉,分组中其它消费者会接替挂掉的消费者继续消费。【一消息对一消费者,比如个人邮件

广播消费消息发给消费者组中的每一个消费者进行消费。【一消息对多消费者,比如说公告】

RocketMQ如何实现消息订阅和发布?

        就是通过topic tags来进行发送到Broker的队列当中,消费者进行监听对应的队列中的消息,当有消息时就进行消费,如果tags是*且是广播模式,所有订阅了topic的消费者都可以进行消费,如果是clusting,这个消息只能被消费一次。

RocketMQ有哪几种部署类型?分别有什么特点?

RocketMQ有4种部署类型

1)单机模式

单机模式, 即只有一个Broker, 如果Broker宕机了, 会导致RocketMQ服务不可用,单点故障, 不推荐使用

2)集群模式

组成一个集群, 集群每个节点都是Master节点, 配置简单, 性能也是最高, 某节点宕机重启不会影响RocketMQ服务

缺点:如果某个节点宕机了, 会导致该节点存在未被消费的消息在节点恢复之前不能被消费

3)多Master多Slave模式,异步复制【消息容忍少量丢失-建议使用】

每个Master配置一个Slave, 多对Master-Slave, Master与Slave消息采用异步复制方式, 主从消息一致只会有毫秒级的延迟。

优点是弥补了多Master模式(无slave)下节点宕机后再恢复前不可订阅的问题。在Master宕机后, 消费者还可以从Slave节点进行消费。采用异步模式复制,提升了一定的吞吐量。总结一句就是,采用多Master多Slave模式,异步复制模式进行部署,系统将会有较低的延迟和较高的吞吐量,缺点就是如果Master宕机, 磁盘损坏的情况下, 如果没有及时将消息复制到Slave, 会导致有少量消息丢失

4)多Master多Slave模式,同步双写【消息重要不能丢失-建议使用】

与多Master多Slave模式,异步复制方式基本一致,唯一不同的是消息复制采用同步方式只有master和slave都写成功以后,才会向客户端返回成功

优点:数据与服务都无单点故障,Master宕机情况下,消息无延迟,服务可用性与数据可用性都非常高

缺点就是会降低消息写入的效率,并影响系统的吞吐量

实际部署中,一般会根据业务场景的所需要的性能和消息可靠性等方面来选择后两种

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

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

追问:那么消息会堆积吗?什么时候清理过期消息?

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

检查这个文件最后访问时间

判断是否大于过期时间

指定时间删除,默认凌晨4点

RocketMQ宕机了怎么办【持久化机制】?

当 RocketMQ 宕机时,需要根据具体的情况采取相应的措施来恢复服务和确保数据不丢失。以下是一些应对 RocketMQ 宕机的常见方法:

  1. 故障诊断

    • 首先,需要确认 RocketMQ 的宕机原因,可能是硬件故障、网络问题、配置错误等等。通过日志、监控工具等来进行故障诊断。
  2. 故障修复

    • 如果宕机是由于配置问题、程序错误等引起的,可以根据错误信息进行修复。如果是硬件故障,可能需要更换硬件。
  3. 数据恢复

    • 如果宕机后RocketMQ中有数据丢失,可以通过 RocketMQ 的持久化机制来恢复数据。RocketMQ 使用 Write-Ahead-Log(WAL-预写式)技术,确保消息在写入磁盘之前被记录下来。在宕机后,可以通过重新读取 WAL 日志来恢复数据
  4. 主从切换

    • 如果 RocketMQ 部署了主从复制模式,当主节点宕机时,可以将从节点切换为主节点,确保服务的可用性
  5. 数据备份和容灾

    • 为了防止宕机引起数据丢失,可以采取数据备份和容灾措施。将数据备份到不同的地理位置,确保数据的安全性和可用性。
  6. 监控和告警

    • 部署监控系统,及时监测 RocketMQ 的状态和性能。设置告警规则,一旦出现异常情况,能够及时通知管理员采取措施。
  7. 灾难恢复计划

    • 制定灾难恢复计划,包括应急联系人、恢复步骤、数据恢复策略等,以便在宕机时能够迅速响应并恢复服务。

总之,应对 RocketMQ 宕机需要根据实际情况来采取相应的措施。重要的是要保持备份和容灾意识,以及定期进行故障模拟和演练,确保在出现问题时能够迅速恢复服务。

RocketMQ是如何保证消息顺序存储和顺序消费的?

RocketMQ按照发送的顺序进行消费遵循(FIFO)原则, 默认生产者以Round Robin轮询方式【算法技术】消息发送到不同的Queue队列中消费者从多个队列中消费消息,这种情况没法保证顺序

【面试】浅学RocketMQ_第3张图片

RocketMQ因此就出现了全局有序和部分有序来解决这个问题:

全局有序:如果要保证消息的全局有序,首先只能由一个生产者往Topic发送消息,并且一个Topic内部只能有一个队列(分区)。消费者也必须是单线程消费这个队列【意思就是一个生成者只能向一个消息队列中发送消息,然后一个消费者进行消费,这样明显性能不是很高。】

部分有序:【部分有序我们就可以将Topic内部划分成我们需要的队列数,把消息通过特定的策略(队列有top和tags,发送给固定的队列,单线程只能消费这个队列中的消息)发往固定的队列中,然后每个队列对应一个单线程处理的消费者。这样即完成了部分有序的需求,又可以通过队列数量的并发来提高消息处理效率。比如:保证同一个订单ID的生成、付款、发货消息按照顺序消费即可实现【不可能先发货再生成订单】

【面试】浅学RocketMQ_第4张图片

我的归纳总结:要保证顺序消费的条件就是Producer生产的消息要保证发送是有序的,且只往一个Queue队列中发送(Topic、Tags),Conusmer消费者保证只消费同一个队列。

注意:一个队列Queue中的消息只能被一个消费者消费,但是一个消费者可以消费多个队列中的消息。【意思就是三个队列只有两个消费者,其中一个消费者消费两个队列中的消息。】

RocketMQ延迟消息

延迟消息即:把消息写到Broker中的队列中后需要延迟一定时间才能被消费 , 在RocketMQ中消息的延迟时间不能任意指定,而是由特定的等级MessageDelayLevel(1 到 18)来指定

//延迟级别 3,代表 10s延迟 message.setDelayTimeLevel(3)【底层使用的是一个定时器DeliverDelayMessageTimerTask】;可以设置消息的过期时间等级【默认18个等级】,可以用作VIP过期支付超时自动取消,淘宝进京东自动确认收货等。

Message msg = new Message(topic, tags, keys, body);

msg.setDelayTimeLevel(3);

===========RocketMQ封装后==============

          // 延迟消息
SendResult sendResult = rocketMQTemplate.syncSend(
        // 事务消息的topic和tags
        "paytimeout_topic:paytimeout_tags",
        // 携带订单号进行消费
        MessageBuilder.withPayload(courseOrder.getOrderNo()).build(),
        // 这个是producer向mq发送2秒不成功就会超时
        2000,
        // 延迟等级 3-10秒 3秒后就超时了
        10
);

如何设置固定时间执行一个方法?

可以使用Timer定时器或者使用TimerTask定时执行方法。

Java代码三种方式实现定时执行某个方法_GuGuBirdXXXX的博客-CSDN博客

RocketMQ事务消息的使用场景

需要操作两个数据库,比如地铁的进出站【上海metro大都会和支付宝两个系统开闸和扣费分离开了,但是保证同时成功和同时失败】,保证 事务消息发送和 本地事务 的原子性【要么都成功要么都失败。】

地铁的进出站和扣费,跨行转账,等场景都可以考虑最终一致性。

RocketMQ事务消息工作原理

【面试】浅学RocketMQ_第5张图片

事务消息解决的就是,就是保证本地事务的状态和事务消息发送的原子性(要么都成功,要么都失败)

  1. 首先事务发起方【producer】:往MQServer【MQ】中发送一个事务消息-half半消息(准备消息),该消息不可被消费(不会暴漏给consumer),MQServer接收到half版消息成功过后,并返回给MQ发送方收到消息的信息。

  2. 然后事务发送方Producer执行本地事务【地铁进站】,执行成功并发送commit给MQServer,如果是执行失败就会发送rollback给MQServer

  3. MQServer接收到commit指令,代表本地事务执行成功,之前的half消息就会发送给consumer进行消费【去进行扣款】,否则就是Rollback消息,进行回滚。

  4. 如果是commit消息,消费者会消费这个half消息,然后执行相关的逻辑处理【支付宝扣款】。如果是Rollback消息就不会被消费,并且将half消息丢弃掉。
    ==========================其他场景==================================

  5. 如果MQServer没有接收到producer执行本地事务后发送的commit或者rollback指令,MQServer会向producer调用事务发送方法让producer进行检查,通过返回commit或者rollback来告知MQServer,producer中的本地事务是否执行成功。

RocketMQ如何保证消息不丢失?

RocketMq怎么保证消息不丢失_GuGuBirdXXXX的博客-CSDN博客

如何防止MQ消息重复消费

重复消费,一般时由于消费者消费成功后,在给MQ返回消费成功消息的时候出现了网络波动,MQ没有接到确认消息,就会继续给消费者投递之前的消息,造成消费者接收到了两条一样的消息。

我们可以通过实现消息的幂等性来避免这种情况【相同的条件参数对同一个方法进行多次调用,只会对执行一次操作】,

比如说让生产者给每个消息携带一个唯一的id【订单号】,消费者获取消息后根据这个id去查询数据库,如果存在了就证明该消息被消费过,就不进行消费。如果不存在就正常消费。

第二种方法就是在produce发送消息的时候就存储消息(消息是唯一的)到Redis当中【只存储一个订单号也行,如果需要对象的信息存对象也行】,当消费者进行消费过后将Redis中的这个消息进行删除,当第一次消费过后,第二次重复消费时会判断redis中没有该消息,就不会进行消费。【不去数据库进行查询。】

你可能感兴趣的:(Interview,java-rocketmq,rocketmq,网络)