MQ

MQ(Message Queue)面试

2020-09-13  本文已影响0人  Djbfifjd

一、什么是 RabbitMQ?

RabbitMQ 是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件),最大的特点就是消费并不需要确保提供方存在,实现了服务之间的高度解耦。RabbitMQ服务器是用 Erlang 语言编写的,而集群和故障转移是构建在开放电信平台框架上的。所有主要的编程语言均有与代理接口通讯的客户端库。

二、为什么要使用 RabbitMQ?

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

三、RabbitMQ 的使用场景有哪些?

①【请求削峰】抢购活动,削峰填谷,防止系统崩塌。
②【定时任务】延迟信息处理,比如 10 分钟之后给下单未付款的用户发送邮件提醒。
③【解耦、服务间异步通信】对于新增的功能可以单独写模块扩展,比如用户确认评价之后,新增了给用户返积分的功能,这个时候不用在业务代码里添加新增积分的功能,只需要把新增积分的接口订阅确认评价的消息队列即可,后面再添加任何功能只需要订阅对应的消息队列即可。
④顺序消费

四、如何确保消息正确地发送至 RabbitMQ?如何确保消息接收方消费了消息?

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

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

3️⃣下面罗列几种特殊情况:
①如果消费者接收到消息,在确认之前断开了连接或取消订阅,RabbitMQ 会认为消息没有被分发,然后重新分发给下一个订阅的消费者。(可能存在消息重复消费的隐患,需要去重)
②如果消费者接收到消息却没有确认消息,连接也未断开,则 RabbitMQ 认为该消费者繁忙,将不会给该消费者分发更多的消息。

五、如何避免消息重复投递或重复消费?

MQ 的服务端通常不保证消息不重复,由业务端来去重。主要是消费端业务逻辑保持幂等性;也可以由消息的服务端通过 messageId 保证,但是通常不这么做。在消息生产时,MQ 内部针对每条生产者发送的消息生成一个 inner-msg-id,作为去重的依据(消息投递失败并重传),避免重复的消息进入队列;在消息消费时,要求消息体中必须要有一个 bizId (对于同一业务全局唯一,如支付ID、订单ID、帖子ID等)作为去重的依据,避免同一条消息被重复消费。

六、消息基于什么传输?

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

七、RabbitMQ 的消息是怎么发送的?

首先客户端必须连接到 RabbitMQ 服务器才能发布和消费消息,客户端和 rabbit server 之间会创建一个 TCP 连接,一旦 TCP 打开并通过了认证(认证就是你发送给 rabbit 服务器的用户名和密码),你的客户端和 RabbitMQ 就创建了一条 amqp 信道(channel),信道是创建在“真实” TCP 上的虚拟连接,amqp 命令都是通过信道发送出去的,每个信道都会有一个唯一的 id,不论是发布消息,订阅队列都是通过这个信道完成的。若该队列至少有一个消费者订阅,消息将以循环(round-robin)的方式发送给消费者。每条消息只会分发给一个订阅的消费者(前提是消费者能够正常处理消息并进行确认)。通过路由可实现多消费的功能。

八、消息怎么路由?

消息提供方--->路由--->一至多个队列。消息发布到交换器时,消息将拥有一个路由键(routing key),在消息创建时设定。通过队列路由键,可以把队列绑定到交换器上。消息到达交换器后,RabbitMQ 会将消息的路由键与队列的路由键进行匹配(针对不同的交换器有不同的路由规则);常用的交换器(广播类型)主要分为以下四种:
①direct(默认):最基础最简单的模式,发送方把消息发送给订阅方,如果有多个订阅者,默认采取轮询的方式进行消息发送。如果路由键完全匹配,消息就被投递到相应的队列。
②headers:与 direct 类似,只是性能很差,此类型几乎用不到。
③fanout:分发模式,把消费分发给所有订阅者。如果交换器收到消息,将会广播到所有绑定的队列上。
④topic:匹配订阅模式,使用正则匹配到消息队列,能匹配到的都能接收到。可以使来自不同源头的消息能够到达同一个队列。 使用 topic 交换器时,可以使用通配符。

九、RabbitMQ 怎么避免消息丢失?

消息持久化,当然前提是队列必须持久化。RabbitMQ 确保持久性消息能从服务器重启中恢复的方式是,将它们写入磁盘上的一个持久化日志文件,当发布一条持久性消息到持久交换器上时,Rabbit 会在消息提交到日志文件后才发送响应。一旦消费者从持久队列中消费了一条持久化消息,RabbitMQ 会在持久化日志中把这条消息标记为等待垃圾收集。如果持久化消息在被消费之前 RabbitMQ 重启,那么 Rabbit 会自动重建交换器和队列(以及绑定),并重新发布持久化日志文件中的消息到合适的队列。每个集群中至少有一个物理磁盘,保证消息落入磁盘。

十、RabbitMQ 持久化有什么缺点?

持久化的缺点就是降低了服务器的吞吐量,因为使用的是磁盘而非内存存储,从而降低了吞吐量。可尽量使用 ssd 硬盘来缓解吞吐量的问题。

十一、要保证消息持久化成功的条件有哪些?

①声明队列必须设置持久化 durable 设置为 true.
②消息推送投递模式必须设置持久化,deliveryMode 设置为 2(持久)。
③消息已经到达持久化交换器。
④消息已经到达持久化队列。

以上四个条件都满足才能保证消息持久化成功。

十二、MQ的缺点

1️⃣系统可用性降低
系统引入的外部依赖越多,越容易挂掉。本来A系统调用BCD三个系统的接口就好了,ABCD四个系统好好的,没啥问题,硬加个 MQ,MQ 挂了,整套系统就崩溃了,风险很大。

2️⃣系统复杂性提高
硬生生加个 MQ 进来,如何保证消息没有重复消费?如何处理消息丢失的情况?如何保证消息传递的顺序性?

3️⃣一致性问题
A系统处理完了直接返回成功了,人都以为这个请求就成功了;但要是BCD三个系统那里,BD两个系统写库成功了,结果C系统写库失败了,这数据就不一致了。所以消息队列实际是一种非常复杂的架构,引入它有很多好处,但是也得针对它带来的坏处做各种额外的技术方案和架构来规避掉,系统复杂度提升了一个数量级,也许是复杂了 10 倍。

十三、RabbitMQ 节点的类型有哪些?

磁盘节点:消息会存储到磁盘。
内存节点:消息都存储在内存中,重启服务器消息丢失,性能高于磁盘类型。

十四、RabbitMQ 集群中唯一一个磁盘节点崩溃了会发生什么情况?

如果唯一磁盘的磁盘节点崩溃了,不能进行以下操作:
①不能创建队列
②不能创建交换器
③不能创建绑定
④不能添加用户
⑤不能更改权限
⑥不能添加和删除集群节点
唯一磁盘节点崩溃了,集群是可以保持运行的,但不能更改任何东西。

十五、RabbitMQ 每个节点是其他节点的完整拷贝吗?为什么?

不是,原因有以下两个:
①存储空间的考虑:如果每个节点都拥有所有队列的完全拷贝,这样新增节点不但没有新增存储空间,反而增加了更多的冗余数据;
②性能的考虑:如果每条消息都需要完整拷贝到每一个集群节点,那新增节点并没有提升处理消息的能力,最多是保持和单节点相同的性能甚至是更糟。

十六、RabbitMQ 的集群镜像集群模式

无论元数据还是 queue 里的消息都会存在于多个实例上,然后每次写消息到 queue 的时候,都会自动到多个实例的 queue 里进行消息同步。

好处在于:任何一个机器宕机了,别的机器都可以用。
坏处在于:①性能开销太大,消息同步所有机器,导致网络带宽压力和消耗很重。②没有扩展性可言,如果某个 queue 负载很重,加机器,新增的机器也包含了这个 queue 的所有数据,并没有办法线性扩展 queue。

十七、RabbitMQ 集群有什么用?

集群主要有以下两个用途:
高可用:某个服务器出现问题,整个 RabbitMQ 还可以继续使用;
高容量:集群可以承载更多的消息量。

十八、RabbitMQ 集群搭建需要注意哪些问题?

各节点之间使用“–link”连接,此属性不能忽略。
各节点使用的 erlang cookie 值必须相同,此值相当于“秘钥”的功能,用于各节点的认证。
整个集群中必须包含一个磁盘节点。

十九、RabbitMQ 对集群节点停止顺序有要求吗?

RabbitMQ 对集群的停止的顺序是有要求的,应该先关闭内存节点,最后再关闭磁盘节点。如果顺序恰好相反的话,可能会造成消息的丢失。

二十、RabbitMQ 中 vhost 的作用是什么?

vhost:每个 RabbitMQ 都能创建很多 vhost,称之为虚拟主机,每个虚拟主机其实都是 mini 版的RabbitMQ,它拥有自己的队列,交换器和绑定,拥有自己的权限机制。

二十一、RabbitMQ 怎么实现延迟消息队列?

延迟队列的实现有两种方式:
①通过消息过期后进入死信交换器,再由交换器转发到延迟消费队列,实现延迟功能。
②使用 RabbitMQ-delayed-message-exchange 插件实现延迟功能。

二十二、RabbitMQ 有哪些重要的角色?

RabbitMQ 中重要的角色有:生产者、消费者和代理:

①生产者:消息的创建者,负责创建和推送数据到消息服务器。
②消费者:消息的接收方,用于处理数据和确认消息。
③代理:就是 RabbitMQ 本身,用于扮演“快递”的角色,本身不生产消息,只是扮演“快递”的角色。

二十三、RabbitMQ 有哪些重要的组件?

①ConnectionFactory(连接管理器):应用程序与 Rabbit 之间建立连接的管理器,程序代码中使用。
②Channel(信道):消息推送使用的通道。
③Exchange(交换器):用于接受、分配消息。
④Queue(队列):用于存储生产者的消息。
⑤RoutingKey(路由键):用于把生成者的数据分配到交换器上。
⑥BindingKey(绑定键):用于把交换器的消息绑定到队列上。

二十四、请列举出可能发生消息重复消费的场景

①网络出现抖动。
②消费端任务执行超时。
③消费端出现异常。

二十五、业务端如何保证幂等性

①使用唯一字段,建立唯一性索引。
②多版本控制。
③状态机幂等。
④保证数据唯一性的其它方式,比如通过 redis、zk 等方式。

二十六、消息队列消息推拉模式是什么意思,分别在什么场景下适用

①推:服务端主动;拉:消费端主动。
②看消费端的消费能力,处理复杂时间长的适合拉模式,推模式实时性响应更好;其中推对于消费端要求更高,拉对于 MQ 服务端的堆积能力要求较高。

二十七、消息的可靠性怎么是保证的

①生产者端:通过同步发送消息,收到服务端的确认写入并存储,来保证一致性;rabbitmq 还支持先写入本地磁盘再发送给服务端
②服务器端:通过把消息写入磁盘、数据库、分布式存储系统中,保证服务端的可靠性
③消费端:通过消费端确认消费成功,才进行下一条消费;通过消息重试保证最终会被消费;

二十八、顺序消息的实现原理

①同一队列中保持有序。
②相同业务的 ID 分配到相同的队列。

二十九、假设发送一条消息需要2ms,如何使用最短的时间完成300条消息的发送

创建多个 producter,然后使用一个线程池进行分组发送。

上一篇下一篇

猜你喜欢

热点阅读