消息中间件RabbitMQ的面试

您所在的位置:网站首页 技能用英文缩写怎么写 消息中间件RabbitMQ的面试

消息中间件RabbitMQ的面试

2024-05-27 22:48| 来源: 网络整理| 查看: 265

   最近在找工作面试,由于在简历中提到了RabbitMQ,所以每次面试都被问到MQ消息中间件的问题。

   简历中的项目我是这样介绍的:经过了多次拆分改造,公司的系统由一个ssh的单体项目,逐步演化为十多个系统的springMVC+springCloud微服务的混合架构,消息中间件也从最初的ActiveMQ切换为现在的RabbitMQ。于是面试的问题来了。

11月13日, M大厂的GYL部门面试 RabbitMQ在项目中有哪些应用?

RabbitMQ的主要应用场景:异步消息、解耦、流量削峰、广播,以实现高性能,高可用,可伸缩和最终一致性架构。

1、异步处理

     比如有些通知类的接口,在某个操作完成后,需要通知第三方状态变化或者发送短信、邮件等操作,无需等待第三方返回结果,并行完成,减少页面端操作时间。

     应用内的同步变异步,比如订单处理,就可以由前端应用将订单信息放到队列,后端应用从队列里依次获得消息处理,高峰时的大量订单可以积压在队列里慢慢处理掉。由于同步通常意味着阻塞,而大量线程的阻塞会降低计算机的性能。

2、应用解耦 比如长流程的客户操作,涉及多个系统,如果实时接口调用,只要某个节点出现问题,客户就无法操作,使用消息可以解除耦合,达到最终一致性。

3、流量控制 比如我们有一个操作是批量短信提醒,如果实时调用短信平台,会导致批量发起时等待时间过长,短信平台会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,在应用前端加入消息队列。业务系统的短信请求在MQ服务器接收后,首先写入消息队列,再按顺序作后续处理,不影响业务系统的后续操作。

 4、广播:基于发布/订阅模式 Publish/Subscribe实现一对多通信

RabbitMQ 出现消息积压的场景怎么解决的?

    消息积压的问题是消费端出了问题,不消费了或者消费的速度极其慢。可能消息队列集群的磁盘都快写满了,都没人消费,这个时候怎么办?或者是这整个就积压了几个小时,你这个时候怎么办?或者是你积压的时间太长了,导致比如 RabbitMQ 设置了消息过期时间后就没了怎么办?

    1. 首先在代码评审时,就要尽量避免此类事故出现。比如通过上线前压力测试,测试监听端的处理速度,按照当前的业务量分析、评估出现积压的可能性。

    2. 如果问题已经出现了,首先分析原因,通常原因有以下几种,比如消费端数据库宕机,消费端受三方接口影响降速了,三方接口不通,

       对于降速的情况,如果条件允许的情况,评估积压的数据量,作一个紧急扩容。

       对于数据库宕掉的情况,实时接口肯定也受到影响,应该先处理数据库问题。

       对于三方宕掉的情况,确认三方的恢复情况,根据实际业务确认是否需要丢掉消息,等三方恢复后重新发起。

    3. 还有一种情况,就是设置了消息超时时间,如果消息积压过程中消息到了过期时间被销毁了,这种需要在积压解决后根据发起方与监听方的实际业务查询问题数据,确认是否需要重新补发。

    4. 积压消息长时间没有处理,mq放不下了,这种情况一般是前面的方案还是执行太慢,这种时候只好采用 “丢弃+批量重导” 的方式来解决了。首先,临时写个程序,连接到mq里面消费数据,收到消息之后直接将其丢弃,快速消费掉积压的消息,降低MQ的压力,然后根据发起方与监听方的实际业务查询问题数据,确认是否需要重新补发或者SQL修复。

 

11月16日 J 大厂的WL部门电话面试 简单描述一下RabbitMQ的底层实现?

RabbitMQ是一个由erlang开发的, 即一个消息队列,主要是用来实现应用程序的异步和解耦,同时也能起到消息缓冲,消息分发的作用。

RabbitMQ是实现 AMQP(高级消息队列协议)的消息中间件的一种,会有三个概念: 发消息者、队列、收消息者。

RabbitMQ信道channcel虚拟连接,建立在上面TCP连接的基础上,数据流动都是通过Channel来进行的,

实现流程:发消息者和队列之间, 加入了交换器 (Exchange), 这样发消息者和队列就没有直接联系, 转而变成发消息者把消息给交换器,交换器根据调度策略再把消息再给队列。

分别为:虚拟主机,交换机Exchange,队列Queue,和绑定Bingings。

虚拟主机:一个虚拟主机持有一组交换机、队列和绑定。为什么需要多个虚拟主机呢?很简单, RabbitMQ 当中,用户只能在虚拟主机的粒度进行权限控制。 因此,如果需要禁止A组访问B组的交换机/队列/绑定,必须为A和B分别创建一个虚拟主机。每一个 RabbitMQ 服务器都有一个默认的虚拟主机。

交换机:Exchange 用于转发消息,但是它不会做存储 ,如果没有 Queue bind 到 Exchange 的话,它会直接丢弃掉 Producer 发送过来的消息。

这里有一个比较重要的概念:路由键。消息到交换机的时候,交互机会转发到对应的队列中,那么究竟转发到哪个队列,就要根据该路由键。

绑定:绑定(Binding)Exchange和Queue的同时,一般会指定一个Binding Key,生产者将消息发送给Exchange的时候,一般会产生一个Routing Key,当Routing Key和Binding Key对应上的时候,消息就会发送到对应的Queue中去。

 

什么是RabbitMQ的死信队列?

    死信,顾名思义就是无法被消费的消息,字面意思可以这样理解,一般来说,producer将消息投递到broker或者直接到queue里了,consumer从queue取出消息进行消费,但某些时候由于特定的原因导致queue中的某些消息无法被消费,这样的消息如果没有后续的处理,就变成了死信,有死信,自然就有了死信队列。“死信”消息会被RabbitMQ进行特殊处理,如果配置了死信队列信息,那么该消息将会被丢进死信队列中,如果没有配置,则该消息将会被丢弃。

消息变成死信的情况有哪些?

对RabbitMQ来说,产生死信的来源大致有如下几种:

消息被拒绝(basic.reject或basic.nack)并且requeue=false.消息TTL过期队列达到最大长度(队列满了,无法再添加数据到mq中) 死信队列的处理方式有哪些?

死信的产生既然不可避免,那么就需要从实际的业务角度和场景出发,对这些死信进行后续的处理,常见的处理方式大致有下面几种,

丢弃,如果不是很重要,可以选择丢弃记录死信入库,然后做后续的业务分析或处理

通过死信队列,由负责监听死信的应用程序进行处理

RabbitMQ的应用场景有哪些?

    RabbitMQ的应用场景有哪些? 这个跟M大厂的第一个问题(RabbitMQ在项目中有哪些应用?)基本一样。

 RabbitMQ如何防止消息丢失?

   1. 消息确认机制(ACK)

   RabbitMQ有一个ACK机制,消费者在接收到消息后会向mq服务发送回执ACK,告知消息已被接收。这种ACK分为两种情况:

   自动ACK:消息一旦被接收,消费者会自动发送ACK

   手动ACK:消息接收后,不会自动发送ACK,而是需要手动发送ACK

   如果消费者没有发送ACK,则消息会一直保留在队列中,等待下次接收。但这里存在一个问题,就是一旦消费者发送了ACK,如果消费者后面宕机,则消息会丢失。因此自动ACK不能保证消费者在接收到消息之后能够正常完成业务功能,因此需要在消息被充分利用之后,手动ACK确认。

    2. 持久化

    消息确认机制(ACK)能够保证消费者不丢失消息,但假如消费者在获取消息之前mq服务宕机,则消息也会丢失,因此要保证消息在服务端不丢失,则需要将消息进行持久化。队列、交换机、消息都要持久化。

   3. 生产者确认

   生产者在发送消息过程中也可能出现错误或者网络延迟灯故障,导致消息未成功发送到交换机或者队列,或重复发送消息,为了解决这个问题,rabbitmq中有多个解决办法:

    事务:用事务将消息发送代码包围起来

   Confirm模式:在发送代码前执行channel.confirmSelect(),如果消息未正常发送,就会进入if waitForConfirm代码块,可以进行重发也可以对失败消息进行记录

   异步confirm方法:生产者发送消息后不用等待服务端回馈发送状态,可以继续执行后面的代码,对于失败消息重发进行异步处理,addConfirmListener

   Spring AMQP中添加配置:生产者确认机制,确保消息正确发送,如果发送失败会有错误回执,从而触发重试

 

11月20日,接到J大厂XLS部门的电话面试 ActiveMQ为什么切换到RabbitMQ?

   由于介绍项目的时候提到“消息中间件从最初的ActiveMQ切换为现在的RabbitMQ”,所以自然出现了这个问题。

有如下几个切换原因:

1. 首先是业务并发量的要求,RabbitMQ支持更高的并发,相对于ActiveMQ在高并发情况下有性能更好。

2. 其次是更方便的对多语言的支持。随着业务增加,有的项目组引入了python团队和项目,ActiveMQ对多语言支持较差。

3. 维护方便,ActiveMQ需要借助zookeeper搭建集群环境,而RabbitMQ内部已经实现了集群,不需要手动搭建集群,集群部署简单。正是因为erlang使得集群部署变得简单。

4. 社区活跃度高,正是因为RabbitMQ文档较多,性能文档,对于小公司(上一个公司)这个体量是首选。

    2013年入职的时候,当时只有一个单体项目,所有功能都是一个ssh项目完成,所以选择了传统的消息队列,使用Java语言编写。基于JMS(Java Message Service),采用多线程并发,资源消耗比较大。所以从2016年开始调研,确认后按项目按功能进行了更换。

ActiveMQ与RabbitMQ有什么不同呢?

主要不同有以下几点:

实现语言不同

     ActiveMq是传统的消息队列,使用Java语言编写。基于JMS(Java Message Service),采用多线程并发,资源消耗比较大。支持P2P和发布订阅两种模式,如果使用java语言开发项目,可以考虑使用activeMQ。

    RabbitMQ是使用Erlang语言编写,并且基于AMQP协议实现,支持多种场景,社区活跃量大。高性能,高可用,支持海量数据。

使用消息中间件什么场景下有丢消息的情况?

丢消息的情况主要有以下几点:

网络原因,如果出现了网络抖动或者通信异常等问题,消息就有可能会丢失客户端收到消息,暂存在内存中还未消费,但是rabbitMQ自己挂了,内存中的数据丢失;消费者成功从rabbitMQ中获取到了消息,还没有将消息完全消费完的时候,就通知rabbitMQ我已经将消息消费了,然后消费者宕机,但是RocketMQ认为消费者已经成功消费了数据,所以数据依旧丢失了。

怎么保证消息发出去,接收端能接收到?

这个问题跟WL部门的( RabbitMQ如何防止消息丢失?)问题有点差不多,但面试官给的提示更偏向实现过程。

为了防止消息丢失。消息丢失分为发送丢失和消费者处理丢失,相应的也有两种确认机制。

一种是消息发送确认。

这种是用来确认生产者将消息发送给交换器,交换器传递给队列的过程中,消息是否成功投递。发送确认分为两步,一是确认是否到达交换器,二是确认是否到达队列。

(1)ConfirmCallback

通过实现ConfirmCallBack接口,消息发送到交换器Exchange后触发回调。使用该功能需要开启确认,spring-boot中配置如下:  spring.rabbitmq.publisher-confirms = true

 

(2)ReturnCallback

通过实现ReturnCallback接口,如果消息从交换器发送到对应队列失败时触发(比如根据发送消息时指定的routingKey找不到队列时会触发)使用该功能需要开启确认,spring-boot中配置如下:

spring.rabbitmq.publisher-returns = true

 

 

第二种是消费接收确认。

这种是确认消费者是否成功消费了队列中的消息。

消费者确认发生在监听队列的消费者处理业务失败,如,发生了异常,不符合要求的数据……,这些场景我们就需要手动处理,比如重新发送或者丢弃。

我们知道ACK是默认是自动的,自动确认会在消息发送给消费者后立即确认,但存在丢失消息的可能,如果消费端消费逻辑抛出异常,加入你用回滚了也只是保证了数据的一致性,但是消息还是丢了,也就是消费端没有处理成功这条消息,那么就相当于丢失了消息。

消息确认模式有:

AcknowledgeMode.NONE:自动确认

AcknowledgeMode.AUTO:根据情况确认

AcknowledgeMode.MANUAL:手动确认

需要在消费者的配置里加手动 ack(确认)则需要修改确认模式为 manual,手动确认的方式有很多,可以在RabbitListenerContainerFactory类进行设置。

spring.rabbitmq.listener.direct.acknowledge-mode=MANUAL RabbitMQ发送消息的模型有哪些?

我感觉我没面试几家,但是RabbitMQ的问题都快被问完了。

第一种模型(HelloWorld),是基本消息模型(basic queues), 功能:一个生产者P发送消息到队列Q,一个消费者C接收。实现了基本的消息的生产和消费。一对一。

 P代表生产者用来生产消息,发送给消费者C,中间的共色部分代表消息队列,用来缓存消息。

第二种模型(Work queue)C1和C2都是消费者,P代表生产者,中间红色的部分是消息队列。Work queue被称为任务队列。当消息处理比较耗时时,生产的速度大于消费的速度,长此以往,消息会在消息队列中越来愈多,无法及时处理。此时可以使用work模型,让多了消费者绑定到一个队列中,共同消费队列中的消息。

功能:一个生产者,多个消费者。写法与基本消息模型类似,只不过原来是一个消费者,现在是多个消费者。多个消费者处理队列中的数据。

特点:1)可以有多个消费者; 2)一条消息只能被多个消费者中的一个消费。

 

第三种模型(Fanout)  发布/订阅模式 Publish/Subscribe, 扇出(fanout)又称广播,广播模式下,消息发送流程

可以有多个消费者每个消费者都有自己的队列每个队列都要绑定到Exchange(交换机)生产者发送消息,只能发送给交换机,交换机决定要发送到哪个队列,生产者无法决定交换机把消息发送给绑定过的所有队列队列的消费者都能够拿到消息,实现一条消息被多个消费者消费 代码实现

功能:一个生产者发送的消息会被多个消费者获取。一个生产者、一个交换机、多个队列、多个消费者,与工作队列区别: 1)工作队列只有一个队列,而发布订阅有多个队列

2)工作队列一个消息只能被多个消费者中的一个消费,而发布订阅一个消息会被多个订阅的消费者消费。

3)发布订阅比工作队列多出一个交换机概念,用来绑定消息发送到哪些消费者。 其实之前的两种模式也需要交换机,其使用默认交换,我们通过空字符串(“”)来识别。

 

第四种模式(Routing) 路由模式(Routing)

功能:生产者发送消息到交换机并且要指定路由key,消费者将队列绑定到交换机时需要指定路由key。只有当两个key相匹配时,消息才会发送到对应的消费者队列。即在广播的基础上有了路由的功能。 type 指定为direct。

Routing订阅模式-Direct(直连)在Fanout模式中,一条消息,会被所有订阅的队列都消费但是在某些场景下,我们希望不同的消息被不同的队列消费。这时候就要用到Direct类型的Exchange。 在Direct下: 1、队列与交换机的绑定不能是任意绑定,而是要指定一个RoutingKey(路由Key) 2、消息的发送方在向交换机发送消息时,需要指定RoutingKey。 3、Exchange不在把消息交给每一个绑定的队列,而是根据消息的Routing Key进行判断,只有队列的RoutingKey与消息的Routing Key完全一致才会接收到消息。

第五种模型(Topic)主题订阅模式, Topic模型的Exchange与Direct相比,是可以根据RoutingKey的不同把消息路由到不同的队列。只不过Topic类型的Exchange可以让队列在绑定路由key的时候使用通配符,这种模型的路由key一般都是由一个或多个单词组成,多个单词之间以“.”分割。

 功能:生产者P发送消息到交换机X,type=topic,交换机根据绑定队列的routing key的值进行通配符匹配; 符号#:匹配一个或者多个词 lazy.# 可以匹配 lazy.irs或者lazy.irs.cor; 符号*:只能匹配一个词 lazy.* 可以匹配 lazy.irs或者lazy.cor

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3