面试-消息队列

创作不易,大家点点关注

1.什么是消息队列?

你可以把消息队列理解为一个使用队列来通信的组件。它的本质,就是个转发器,包含发消息、存消息、消费消息的过程。

这里可以以一种更加容易理解的方式解释消息队列:

想象一下,你经营着一家非常火爆的餐厅。顾客们源源不断地进来点餐(这就好比是系统中的各种请求或事件发生,产生了需要处理的 “消息”)。

以前,服务员(就像系统里直接处理请求的部分)得同时兼顾接待新顾客、记录菜单、把菜单送到厨房,还得关注每个菜品做没做好、给顾客上菜,忙得晕头转向。要是顾客太多,服务员就可能手忙脚乱,甚至把订单弄混或者忘记处理。

后来,你想出了一个办法,在餐厅入口放了一个专门记录订单的本子(这就是消息队列)。顾客进来后,服务员只需要快速把顾客点的餐写在本子上(生产者往消息队列里发送消息),然后就可以去招呼下一位顾客了。

厨房的厨师(消费者)呢,就按本子上记录的顺序,一道一道地做菜(从消息队列里按顺序取出消息并处理)。这样一来,服务员不用再操心每个订单处理得怎么样了,专心接待新顾客就行;厨师也不用被服务员催来催去,可以有条不紊地做菜。即使突然来了一大批顾客,订单都写在本子上,也不会出现混乱,大家都能各司其职。

2.消息队列的选择

Kafka、ActiveMQ、RabbitMQ、RocketMQ来进行不同维度对比。

特性 ActiveMQ RabbitMQ RocketMQ Kafka
单机吞吐量 万级 万级 10 万级 10 万级
时效性 毫秒级 微秒级 毫秒级 毫秒级
可用性 高(主从) 高(主从) 非常高(分布式) 非常高(分布式)
消息重复 至少一次 至少一次 至少一次 最多一次 至少一次最多一次
消息顺序性 有序 有序 有序 分区有序
支持主题数 千级 百万级 千级 百级,多了性能严重下滑
消息回溯 不支持 不支持 支持(按时间回溯) 支持(按offset回溯)
管理界面 普通 普通 完善 普通

3.消息队列的使用场景

  • 解耦:可以在多个系统之间进行解耦,将原本通过网络之间的调用的方式改为使用MQ进行消息的异步通讯,只要该操作不是需要同步的,就可以改为使用MQ进行不同系统之间的联系,这样项目之间不会存在耦合,系统之间不会产生太大的影响,就算一个系统挂了,也只是消息挤压在MQ里面没人进行消费而已,不会对其他的系统产生影响。

  • 异步:加入一个操作设计到好几个步骤,这些步骤之间不需要同步完成,比如客户去创建了一个订单,还要去客户轨迹系统添加一条轨迹、去库存系统更新库存、去客户系统修改客户的状态等等。这样如果这个系统都直接进行调用,那么将会产生大量的时间,这样对于客户是无法接收的;并且像添加客户轨迹这种操作是不需要去同步操作的,如果使用MQ将客户创建订单时,将后面的轨迹、库存、状态等信息的更新全都放到MQ里面然后去异步操作,这样就可加快系统的访问速度,提供更好的客户体验。

  • 削峰:一个系统访问流量有高峰时期,也有低峰时期,比如说,中午整点有一个抢购活动等等。比如系统平时流量并不高,一秒钟只有100多个并发请求,系统处理没有任何压力,一切风平浪静,到了某个抢购活动时间,系统并发访问了剧增,比如达到了每秒5000个并发请求,而我们的系统每秒只能处理2000个请求,那么由于流量太大,我们的系统、数据库可能就会崩溃。这时如果使用MQ进行流量削峰,将用户的大量消息直接放到MQ里面,然后我们的系统去按自己的最大消费能力去消费这些消息,就可以保证系统的稳定,只是可能要跟进业务逻辑,给用户返回特定页面或者稍后通过其他方式通知其结果。

4.消息重复消费怎么解决?

只有让消费者的处理逻辑具有幂等性,保证无论同一条消息被消费多少次,结果都是一样的,从而避免因重复消费带来的副作用。

对于已经消费成功的消息,本地数据库表或Redis缓存业务标识,每次处理前先进行校验,保证幂等。

5.消息丢失怎么解决的?

使用一个消息队列,其实就分为三大块:生产者、中间件、消费者,所以要保证消息就是保证三个环节都不能丢失数据。

  • 生产者的消息确认:生产者在发送消息时,需要通过消息确认机制来确保消息成功到达。

  • 存储消息:broker收到消息后,需要将消息持久化到磁盘上,避免消息因内存丢失。

  • 消费者的消息确认:消费者在处理完消息后,再向消息队列发送确认(ACK),如果消费者未发送确认,消息队列需要重新投递该消息。

除此之外,如果消费者持续消费失败,消息队列可以自动进行重试机制或将消息发送到死信队列(DLQ)或通过日志等其他手段记录异常的消息,避免因一时的异常导致消息丢失。

6.消息队列的核心术语

  • 生产者:负责向消息队列中发送消息的应用程序或服务。生产者将消息发送到指定的队列或主题,供消费者消费。

  • 消费者:从消息队列中读取和处理消息的应用程序或服务。消费者根据业务逻辑处理收到的消息,并可以向消息队列发送确认。

  • Broker:消息队列的核心组件,负责接收、存储和分发消息。

  • 队列:存储消息的容器,消息按照先进先出的顺序在队列中存储。队列中的每条消息通常只能被一个消费者消费一次。

  • 主题 (Topic):用于在发布/订阅模型中,消息生产者将消息发布到一个主题,多个订阅该主题的消费者可以接收到相同的消息。

  • 死信队列 (Dead Letter Queue):当消息因为消费失败、多次重试后未成功处理、消息过期或队列达到最大长度等原因被丢弃时,消息可以被转移到死信队列中。

7.如何保证消息的有序性?

单一生产者和单一消费者:

  • 使用单个生产者发送消息到单个队列,并由单个消费者处理消息。可以确保消息按照生产者的发送顺序消费。

  • 方法简单但是容易性能瓶颈,无法利用并发的优势。

顺序队列:

  • 一些消息队列(如RabbitMQ)支持顺序队列,消息在队列中的存储顺序与土地顺序一致。如果使用单个顺序队列,消息将按顺序被消费。

  • 可以使用多个顺序队列来提高并发处理能力,并使用特定规则将消息分配到不同的顺序队列中。

分区:

  • Kafka 可以通过将消息划分到同一个分区(Partition)来保证消息在分区内是有序的,消费者按照分区顺序读取消息就可以保证消息顺序。

  • 这也可能会限制消息的并行处理程度,需要在顺序性和吞吐量之间进行权衡。

8.如何处理消息积压问题

消息积压是因为生产者的生产速度,大于消费者的消费速度。遇到消息积压问题时,我们需要先排查,是不是有bug产生了。

如果不是bug,我们可以优化一下消费的逻辑,比如之前是一条一条消息消费处理的话,我们可以确认是不是可以优为批量处理消息。如果还是慢,我们可以考虑水平扩容,增加Topic的队列数,和消费组机器的数量,提升整体消费能力。

常见有以下几种方式提升消费者的消费能力

  1. 增加消费者线程数量:提高并发消费能力。

  2. 增加消费实例:在分布式系统中,可以水平扩展多个消费实例,从而提高消费速率。

  3. 优化消费者逻辑:检查消费者代码,减少单个消息的处理时间。例如:减少I/O操作、使用批量处理等。

除此之外还可以进行限流和降级处理

  • 对消息生产端进行限流,降低生产速率,避免消息积压进一步恶化。

  • 对非关键消息进行丢弃或延迟处理,只保留高优先级的消息,提高系统的响应速度。

9.消息队列设计成推消息还是拉消息?

推模式(Push):消息队列将消息主动推送给消费者,适合实时性要求高、消费者能够及时处理消息的场景。

  • 优点:实时性好,消息可立即送达消费者。

  • 缺点:难以控制消费速度,容易导致消费者过载,尤其是在高并发时。

拉模式(Pull):消费者主动从消息队列中拉取消息,适合消费能力有限、需要根据自身处理能力调控速率的场景。

  • 优点:消费者可以根据自身负载决定拉取速率,避免过载;更适合批量处理。

  • 缺点:可能会导致消息延迟,实时性不如推模式,尤其是拉取频率较低时。

 

你可能感兴趣的:(面试,职场和发展,java,经验分享)