常见面试题
1.MQ的作用及应用场景
类似问题:项目什么情况下用到了MQ,为什么要用MQ
MQ的主要应用场景,消息队列的应用场景,为什么说消息队列可以削峰
首先MQ是一种用来接收和转发消息的队列,常见的应用常见如下:
1)异步解耦:在业务中,一些操作需要消耗大量时间,但是并不需要我们立刻返回结果,我们就可以使用MQ来把操作异步化
2)流量削峰:在访问量突增的情况下,我们应用仍然要保证持续处理请求,但是突然把大量请求发送过来会导致我们的应用崩溃,我们也不可能按照最大访问量来配置机器,所以我们就可以使用MQ来控制流量,把请求排队,等系统根据自己的能力来逐渐处理请求
3)消息分发:当多个系统需要对同一数据做出响应,我们就可以使用MQ进行消息分发,我们这里可以用到广播模式
4)延迟通知:当我们想让对方过一段时间收到消息时,我们可以使用MQ的延迟消息功能,就比如在支付过程中,用户多久没有支付,就会自动取消订单
2.你了解过那些MQ,他们的区别是什么
类似问题:kafka和RabbitMQ的对比,对比其他MQ,不同MQ分别用在什么场景
kafka和rocketmq比较,消息队列除了可以使用RabbitMQ,可以使用RocketMQ吗
首先业界有很多MQ产品,我们这里简单介绍三种,RabbitMQ,RocketMQ,kafka
1)kafka:kafka的功能比较简单,但是效率很高,吞吐量大,可以达到单机10w的吞吐量,主要用来做大数据处理,和日志相关操作的
2)RabbitMQ:使用Erlang开发支持大部分主流语言,功能比较全面,效率虽不如kafka但是也很高,同时吞吐量虽然不大但也达到了单机万级的吞吐量,同时开源提供界面,社区活跃,对于新手十分友好,比较适合于一些并发量和数据量不大的情况
3)RocketMQ:采用Java开发,在可用性,功能以及稳定性方面比较突出,吞吐量能达到单机10w,但是支持的语言不多,且社区活动一般,适用于一些可靠性高,并发量大的场景
3.介绍下RabbitMQ的核心概念及工作流程
相关面试题:RabbitMQ的核心流程简单介绍下,讲下RabbitMQ的结构
RabbitMQ负责接收消息并且转发消息
核心概念:
Producer:生产者,向RabbitMQ发送消息的 Consumer:消费者,从RabbitMQ中获取消息
Broker:消息队列服务器 Vistual Host:虚拟机,逻辑上隔离的,可以用来做业务区分
Connection:网络连接,允许客户端和RabbitMQ进行通信 Channel:信道,发送消息和接收都是通过信道
Exchange:交换机,负责接收生产者的消息,并且根据Routingkey 映射到不同的队列
Queue:队列,用来存储消息直到被消费者消费
工作流程:创建连接,生产者连接到Broker(建立一个Connection和Channel) 声明交换机和队列 发布消息到Broker,消息处理(Broker接收消息并且存入相应队列,如果没有队列可根据return模式进行处理) 消费消息(消费者监听队列,消息到达,从队列获取消息,处理后给MQ返回是否确认)消息删除(确认就删除,不确认或者拒绝,可以重试或者有死信队列放入死信队列,否则丢失消息)
4.RabbitMQ如何保证消息的可靠性
相关面试题:RabbitMQ消息丢失原因及解决方案 , 如何保证消息不丢失,消息写入失败怎么办, 消息消费失败如何处理, MQ的主动ack和被动ack有什么区别,RabbitMQ怎么解决数据丢失问题,如何保证一致性 , 消息队列怎么保证消费者消息不丢失的
这个问题我们要从生产者的可靠性,MQ的可靠性,消费者的可靠性三方面回答
1)生产者的可靠性保障通过confirm模式来保证消息能够正确到达交换机(可能是网络原因也可能是交换机不存在),如果没到达可以选择重新发送或者不发送等等,通过return模式来确保如果消息没有被成功映射到队列上要怎么处理(映射不到队列上可能是Routingkey的原因也有可能是没有这个队列)我们可以加上一个死信队列,这样我们如果映射到的队列满了,也可以在死信队列找到我们的消息不至于丢失
2)MQ的可靠性保障通过消息的持久化,队列的持久化,交换机的持久化来保证,首先就是交换机要设置持久化,否则重启MQ后,该交换机信息就没了,我们需要重新声明,但是我们的消息是不会丢失的,因为消息是存储队列中的,队列的持久化也要记得设置,因为消息存储在队列上,如果队列没有持久化,那么重启MQ消息也会消失,最后就是消息持久化,我们设置消息持久化的前提是要设置队列持久化,否则队列丢了即使消息是持久化的,也会丢失(但是即使我们都设置持久化,也有极小的概率会丢失数据,因为我们队列上的消息写入硬盘,并不是来一个消息写一个,而是有一个缓冲区,我们缓冲区到达一定限制才会一次性写入硬盘,这样也能保证我们的效率)
3)消费者的可靠性主要有自动确认和手动确认两种方式,自动确认是不可靠的,因为自动确认是只要消息到达了消费者就确认,如果我们消费者消费失败,那么我们消息就会丢失,我们可以使用手动确认保证消费者的可靠性,这样如果我们业务有异常或者有网络问题,我们可以不进行确认,我们可以选择重试,或者直接把这条消息放到死信队列,我们可以进行人工处理
5.RabbitMQ怎么保证消息顺序性
相关面试题:RabbitMQ怎么保证消息的顺序性? 如何保证消息能够有序消费?
我们要先说RabbitMQ的顺序性分为局部顺序性和全局顺序性
全局顺序性很难保障而且使用场景很少,我们可以类似于TCP使用序号+消费者缓冲区来实现,通过序号在消费者缓冲区来排列实现有序
那么局部有序就比较好保障,我们可以使用单队列单消费者(这个由我们RabbitMQ自动保障不需要我们额外做处理) 如果我们嫌这样吞吐量比较低,我们可以使用分区消费,我们可以引入一个id,把同样的id(这个同样id是一个概念,也可以是hash过后同样的id)的放到一个队列,然后给这个队列分一个消费者,本质上也是单队列单消费者
但是RabbitMQ本身不支持分区消费,所以需要业务逻辑实现,我们可以使用Spring-cloud-stream来实现
也可以通过业务逻辑判断,比如在消费端内部实现消息排序顺序
6.如果保证消息消费时的幂等性
相关面试题:RabbitMQ怎么保证消息不被重复消费? 消息或者请求存在重复消费问题吗?
怎么解决MQ重复消费的问题?
解决方法:
- 通过引入全局唯一ID:可以是UUID也可以是自增ID,只要保证ID不重复即可,这样我们消费一个消息,就把消息的ID放到数据库中
然后我们每次消费消息时,我们要判断消息的ID是否存在于数据库中,如果不存在,我们就把消息消费后把ID放到数据库,如果存在,我们放弃消费这一条消息
- 业务逻辑判断:在业务逻辑层面实现消息处理的幂等性,就比如我们先判断数据库中是否有相关数据记录,或者使用乐观锁机制避免已被其他事务更改的数据,或者检查相关业务的状态,如果是未处理,我们再进行处理
7.RabbitMQ有那些特性?
1)发送方确认
2)持久化
3)消费者确认
4)重试机制
5)TTL
6)死信队列
7)延迟队列
8.介绍下RabbitMQ的死信队列
相关面试题:RabbitMQ的死信队列以及应用场景
回答问题从以下三个方面来:死信队列的概念,死信的来源,死信队列的应用场景
死信是一种因为一些特殊原因(消息过期,消息被消费者拒绝或者队列满了)而无法被正常消费的消息,死信队列就是用来存储死信的队列
死信的应用场景可以应用在支付场景中
我们用户支付订单,支付系统会给我们订单系统返回当前订单的支付状态
为了保障支付信息不丢失,需要使用死信队列机制,当消息消费异常时,会放到死信队列中(有可能存在用户支付,但是消息没有被消费或者异常拒绝的情况),此时我们放到死信队列中,再对这个数据进行处理(可能是人工确认)
我们也可以通过死信队列来做错误日志收集,或者我们可以使用TTL+死信队列来实现延迟队列的效果
9.介绍下RabbitMQ延迟队列
相关面试题:RabbitMQ延迟队列的实现
我们也可以从三个方面回答:概念 应用场景 实现方式
延迟队列是一种特殊的队列,在消息发送后消费者并不会立刻收到消息,等过一段时间,才让消费者接收并处理消息
我们可以将延迟队列应用到定时发布场景,比如我们要定时发送一篇博客,我们就可以使用延迟队列来发布,也可以用在订单支付,我们用户在点击购买时,我们会设置一个延迟的消息放到延迟队列中,等时间到了用户还没有支付成功,那么就会销毁这个订单,也可用于只能家电的定时功能
我们可以通过TTL+死信队列的方式来实现,也就是通过TTL设置延迟时间,但是用户并不订阅正常的队列,而是订阅死信队列,这样过期时间到了,消息过期会放到死信队列中,然后我们用户就可以在死信队列中拿到消息,实现延迟功能(但是这样会有一些缺陷,也就是顺序问题,这样我们在TTL那篇博客有讲)
我们也可以使用延迟插件来解决 这个插件会给我们提供一个新的交换机,这个交换机可以实现延迟功能
问:TTL+死信和延迟插件的优缺点
TTL+死信:优点:比较灵活,不需要我们引入其他插件
缺点:有消息的顺序问题,可能会有错误
延迟插件:优点:没有消息的顺序问题
缺点:需要我们引入插件,同时会有一些版本问题,需要运维人员
10.介绍下RabbitMQ的工作模式
相关面试题:RabbitMQ的几种模式有哪些
我们看一张图就可以解决
同时我们要知道交换机是有多种的,有dirct(定向),topic(通配符),fanout(广播),headers
11.消息积压的原因,如果处理
相关面试题:MQ消息堆积问题,如何解决MQ的数据囤积
消息积压的原因:消息生产过快,消费者消费能力不足,网络问题,RabbitMQ服务配置低
消费者消费能力不足可能是因为:业务逻辑太复杂,代码的逻辑性能低,消费者异常处理问题,系统资源问题
解决办法:
我们可以通过限制消息的生产速率减轻消息生产速度,设置ttl,把消息转入死信队列等有空时再进行处理
我们消费者优化代码,或者多加几个队列共同消费设置消息分发,当一个消费者阻塞,把消息分发给其他消费者消息发生异常,设置重试策略,或者转入死信队列
如果是资源问题,就只能提高配置
12.RabbitMQ是推模式还是拉模式
RabbitMQ支持推模式(队列把消息主动给消费者)和拉模式(消费者从队列中主动获取消息)
但是主要是基于推模式来工作的(推模式会把队列的所有消息都给消费者),它的核心设计是让消息队列中的消费者接收到生产者生产的所有消息,使用channel.basicConsume方法订阅队列,RabbitMQ就会把消息推送到订阅该队列的消费者,如果只是想获取单条消息,而不是持续订阅就使用我们的拉模式,使用channel.basicGet
总结:
推模式:能够一次性获取到所有的消息,对消息获取更加实时,适合对数据实时性要求比较高的场景,就比如监控系统,报表系统
拉模式:一次只能够拉取到队列的一条消息,可以按照自己的速度来拉取消息,避免消息积压,适合需要流量控制的场景