RabbitMQ面试题及答案

在这里插入图片描述

什么是RabbitMQ?为什么使用RabbitMQ?

答:采用AMQP高级消息队列协议的一种消息队列技术,最大的特点就是消费并不需要确保提供方存在,实现了服务之间的高度解耦

可以用它来:解耦、异步、削峰。

为什么要使用rabbitmq

  1. 在分布式系统下具备异步,削峰,负载均衡等一系列高级功能
  2. 拥有持久化的机制,进程消息,队列中的信息也可以保存下来
  3. 实现消费者和生产者之间的解耦
  4. 对于高并发场景下,利用消息队列可以使得同步访问变为串行访问达到一定量的限流,利于数据库的操作
  5. 可以使用消息队列达到异步下单的效果,排队中,后台进行逻辑下单

使用rabbitmq的场景

  • 服务间异步通信
  • 顺序消费
  • 定时任务
  • 请求削峰

RabbitMQ有什么优缺点?

优点:

  • 解耦
  • 系统A在代码中直接调用系统B和系统C的代码,如果将来D系统接入,系统A还需要修改代码,过于麻烦!

  • 异步
  • 将消息写入消息队列,非必要的业务逻辑以异步的方式运行,加快响应速度

  • 削峰
  • 并发量大的时候,所有的请求直接怼到数据库,造成数据库连接异常

缺点:

  • 降低了系统的稳定性

  • 本来系统运行好好的,现在你非要加入个消息队列进去,那消息队列挂了,你的系统不是呵呵了。因此,系统可用性会降低

  • 增加了系统的复杂性

  • 加入了消息队列,要多考虑很多方面的问题,比如:一致性问题、如何保证消息不被重复消费、如何保证消息可靠性传输等。因此,需要考虑的东西更多,复杂性增大

线程Queue,进程Queue和RabbitMQ区别

  • 进程Queue用于父进程与子进程(或同一父进程中多个子进程)间数据传递
  • python自己的多个进程间交换数据或者与其他语言(如Java)进程queue就无能为力
  • RabbitMQ就是这样一个可以在不同程序间共享数据的代理

RabbitMQ 中的 broker 是指什么?cluster 又是指什么?

  • broker 是指一个或多个 erlang node 的逻辑分组,且 node 上运行着 RabbitMQ 应用程序
  • cluster 是在 broker 的基础之上,增加了 node 之间共享元数据的约束

RabbitMQ 概念里的 channel、exchange 和 queue 是逻辑概念,还是对应着进程实体?分别起什么作用?

  • queue 具有自己的 erlang 进程
  • exchange 内部实现为保存 binding 关系的查找表
  • channel 是实际进行路由工作的实体,即负责按照 routing_key 将 message 投递给 queue

由 AMQP 协议描述可知,channel 是真实 TCP 连接之上的虚拟连接,所有 AMQP 命令都是通过 channel 发送的,且每一个 channel 有唯一的 ID。一个 channel 只能被单独一个操作系统线程使用,故投递到特定 channel 上的 message 是有顺序的。但一个操作系统线程上允许使用多个 channel

消息基于什么传输?

由于TCP连接的创建和销毁开销较大,且并发数受系统资源限制,会造成性能瓶颈。RabbitMQ使用信道的方式来传输数据。信道是建立在真实的TCP连接内的虚拟连接,且每条TCP连接上的信道数量没有限制

消息如何分发?

若该队列至少有一个消费者订阅,消息将以循环(round-robin)的方式发送给消费者。每条消息只会分发给一个订阅的消费者(前提是消费者能够正常处理消息并进行确认)

如何确保消息正确地发送至RabbitMQ?

  • RabbitMQ使用发送方确认模式,确保消息正确地发送到RabbitMQ
  • 发送方确认模式:
  • 将信道设置成confirm模式(发送方确认模式),则所有在信道上发布的消息都会被指派一个唯一的ID
  • 一旦消息被投递到目的队列后,或者消息被写入磁盘后(可持久化的消息),信道会发送一个确认给生产者(包含消息唯一ID)
  • 如果RabbitMQ发生内部错误从而导致消息丢失,会发送一条nack(not acknowledged,未确认)消息
  • 发送方确认模式是异步的,生产者应用程序在等待确认的同时,可以继续发送消息。当确认消息到达生产者应用程序,生产者应用程序的回调方法就会被触发来处理确认消息

如何确保消息接收方消费了消息?

  • 接收方消息确认机制:消费者接收每一条消息后都必须进行确认(消息接收和消息确认是两个不同操作)
  • 只有消费者确认了消息,RabbitMQ才能安全地把消息从队列中删除
    这里并没有用到超时机制,RabbitMQ仅通过Consumer的连接中断来确认是否需要重新发送消息
  • 也就是说,只要连接不中断,RabbitMQ给了Consumer足够长的时间来处理消息

下面列出几种特殊情况:

  • 如果消费者接收到消息,在确认之前断开了连接或取消订阅,RabbitMQ会认为消息没有被分发,然后重新分发给下一个订阅的消费者。(可能存在消息重复消费的隐患,需要根据bizId去重)
  • 如果消费者接收到消息却没有确认消息,连接也未断开,则RabbitMQ认为该消费者繁忙,将不会给该消费者分发更多的消息

如何保证RabbitMQ的高可用?

答:没有哪个项目会搭建一台RabbitMQ服务器提供服务,风险太大;

如何保证RabbitMQ不被重复消费?

先说为什么会重复消费:正常情况下,消费者在消费消息的时候,消费完毕后,会发送一个确认消息给消息队列,消息队列就知道该消息被消费了,就会将该消息从消息队列中删除;

但是因为网络传输等等故障,确认信息没有传送到消息队列,导致消息队列不知道自己已经消费过该消息了,再次将消息分发给其他的消费者

  • 在消息生产时,MQ内部针对每条生产者发送的消息生成一个inner-msg-id,作为去重的依据(消息投递失败并重传),避免重复的消息进入队列
  • 在消息消费时,要求消息体中必须要有一个bizId(对于同一业务全局唯一,如支付ID、订单ID、帖子ID等)作为去重的依据,避免同一条消息被重复消费
  • 保证消息的唯一性,就算是多次传输,不要让消息的多次消费带来影响;保证消息等幂性
  • 在写入消息队列的数据做唯一标示,消费消息时,根据唯一标识判断是否消费过

如何保证RabbitMQ消息的可靠传输?

答:消息不可靠的情况可能是消息丢失,劫持等原因;

丢失又分为:

  • 生产者丢失消息
  • 消息列表丢失消息
  • 消费者丢失消息

生产者丢失消息:
从生产者弄丢数据这个角度来看,RabbitMQ提供transaction和confirm模式来确保生产者不丢消息;

transaction机制就是说:

  • 发送消息前,开启事务(channel.txSelect()),然后发送消息,如果发送过程中出现什么异常,事务就会回滚(channel.txRollback())
  • 如果发送成功则提交事务(channel.txCommit())
  • 这种方式有个缺点:吞吐量下降

confirm模式用的居多:一旦channel进入confirm模式,所有在该信道上发布的消息都将会被指派一个唯一的ID(从1开始),一旦消息被投递到所有匹配的队列之后;

rabbitMQ就会发送一个ACK给生产者(包含消息的唯一ID),这就使得生产者知道消息已经正确到达目的队列了;

如果rabbitMQ没能处理该消息,则会发送一个Nack消息给你,你可以进行重试操作。

消息列表丢失消息:
消息持久化。

处理消息队列丢数据的情况,一般是开启持久化磁盘的配置。

这个持久化配置可以和confirm机制配合使用,你可以在消息持久化磁盘后,再给生产者发送一个Ack信号。

这样,如果消息持久化磁盘之前,rabbitMQ阵亡了,那么生产者收不到Ack信号,生产者会自动重发。

那么如何持久化呢?

这里顺便说一下吧,其实也很容易,就下面两步

将queue的持久化标识durable设置为true,则代表是一个持久的队列
发送消息的时候将deliveryMode=2
这样设置以后,即使rabbitMQ挂了,重启后也能恢复数据

消费者丢失消息:
消费者丢数据一般是因为采用了自动确认消息模式,改为手动确认消息即可!

消费者在收到消息之后,处理消息之前,会自动回复RabbitMQ已收到消息;

如果这时处理消息失败,就会丢失该消息;

解决方案:处理消息成功后,手动回复确认消息。

如何保证RabbitMQ消息的顺序性?

单线程消费保证消息的顺序性;对消息进行编号,消费者处理消息是根据编号处理消息

消息怎么路由?

从概念上来说,消息路由必须有三部分:交换器、路由、绑定
生产者把消息发布到交换器上;绑定决定了消息如何从路由器路由到特定的队列;消息最终到达队列,并被消费者接收。

消息发布到交换器时,消息将拥有一个路由键(routing key),在消息创建时设定。
通过队列路由键,可以把队列绑定到交换器上。

消息到达交换器后,RabbitMQ会将消息的路由键与队列的路由键进行匹配(针对不同的交换器有不同的路由规则)。如果能够匹配到队列,则消息会投递到相应队列中;如果不能匹配到任何队列,消息将进入 “黑洞”。

常用的交换器主要分为一下三种:

  • direct:如果路由键完全匹配,消息就被投递到相应的队列
  • fanout:如果交换器收到消息,将会广播到所有绑定的队列上
  • topic:可以使来自不同源头的消息能够到达同一个队列。使用topic交换器时,可以使用通配符。比如:“*” 匹配特定位置的任意文本, “.” 把路由键分为了几部分,“#” 匹配所有规则等。

特别注意:发往topic交换器的消息不能随意的设置选择键(routing_key),必须是由"."隔开的一系列的标识符组成。

什么是元数据?元数据分为哪些类型?包括哪些内容?与 cluster 相关的元数据有哪些?元数据是如何保存的?元数据在 cluster 中是如何分布的?

在非 cluster 模式下:

元数据主要分为

  • Queue 元数据(queue 名字和属性等)
  • Exchange元数据(exchange 名字、类型和属性等)
  • Binding 元数据(存放路由关系的查找表)
  • Vhost元数据(vhost 范围内针对前三者的名字空间约束和安全属性设置)

在 cluster 模式下:

还包括 cluster 中 node 位置信息和 node 关系信息
元数据按照 erlang node 的类型确定是仅保存于 RAM 中,还是同时保存在 RAM 和 disk 上。元数据在 cluster 中是全 node 分布的。

在单node 系统和多 node 构成的 cluster 系统中声明 queue、exchange ,以及进行 binding 会有什么不同?

当你在单 node 上声明 queue 时,只要该 node 上相关元数据进行了变更,你就会得到 Queue.Declare-ok 回应;而在 cluster 上声明 queue ,则要求 cluster 上的全部 node 都要进行元数据成功更新,才会得到 Queue.Declare-ok 回应。另外,若 node 类型为 RAM node 则变更的数据仅保存在内存中,若类型为 disk node 则还要变更保存在磁盘上的数据。

死信队列&死信交换器:DLX 全称(Dead-Letter-Exchange),称之为死信交换器,当消息变成一个死信之后,如果这个消息所在的队列存在x-dead-letter-exchange参数,那么它会被发送到x-dead-letter-exchange对应值的交换器上,这个交换器就称之为死信交换器,与这个死信交换器绑定的队列就是死信队列

消息在什么时候会变成死信?

  • 消息拒绝并且没有设置重新入队
  • 消息过期
  • 消息堆积,并且队列达到最大长度,先入队的消息会变成DL
发布了81 篇原创文章 · 获赞 65 · 访问量 6180

猜你喜欢

转载自blog.csdn.net/weixin_45139342/article/details/105468382
今日推荐