RabbitMQ面试题

一、为何要引入MQ系统,直接读写数据库不行吗?
其实就是问问你消息队列都有哪些使用场景,而后你项目里具体是什么场景,说说你在这个场景里用消息队列是什么?html

面试官问你这个问题,指望的一个回答是说,大家公司有个什么业务场景,这个业务场景有个什么技术挑战,若是不用 MQ 可能会很麻烦,可是你如今用了 MQ 以后带给了你不少的好处。java

先说一下消息队列常见的使用场景吧,其实场景有不少,可是比较核心的有 3 个:解耦、异步、削峰。
解耦:多系统多进程的数据交换,用pub/sub
异步:把大数据量的同步处理改成异步
削峰:通常的A 系统使用 MySQL,扛到每秒 2k 个请求就差很少了,若是每秒请求到 5k 的话,可能就直接把 MySQL 给打死了,致使系统崩溃,用户也就无法再使用系统了。若是使用 MQ, 每秒 5k 个请求写入 MQ,A 系统每秒钟最多处理 2k 个请求,由于 MySQL 每秒钟最多处理 2k 个。A 系统从 MQ 中慢慢拉取请求,每秒钟就拉取 2k 个请求,不要超过本身每秒能处理的最 大请求数量就 ok,这样下来,哪怕是高峰期的时候,A 系统也绝对不会挂掉,这又设计请求排队的问题。git

二、消息队列有什么优缺点?
优势:解耦、异步、削峰
缺点:
系统可用性下降
系统引入的外部依赖越多,越容易挂掉。原本你就是 A 系统调用 BCD 三个系统的接口就行了,人 ABCD 四个系统好好的,没啥问题,你偏加个 MQ 进来,万一 MQ 挂了咋整,MQ 一挂,整套
系统崩溃的,你不就完了?github

系统复杂度提升
硬生生加个 MQ 进来,你怎么保证消息没有重复消费?怎么处理消息丢失的状况?怎么保证消息传递的顺序性?头大头大,问题一大堆,痛苦不已。面试

一致性问题
A 系统处理完了直接返回成功了,人都觉得你这个请求就成功了;可是问题是,要是 BCD 三个系统那里,BD 两个系统写库成功了,结果 C 系统写库失败了,咋整?你这数据就不一致了。redis

三、Kafka、ActiveMQ、RabbitMQ、RocketMQ 有什么优缺点?
https://blog.csdn.net/Dome_/article/details/84990563数据库

四、RabbitMQ 的高可用性如何保证?
RabbitMQ 有三种模式:单机模式、普通集群模式、镜像集群模式
单机模式不存在高可用。
普通集群模式也不存在高可用性,意思就是在多台机器上启动多个 RabbitMQ 实例,每一个机器启动一个。可是你建立的 queue,只会放在一个 RabbitMQ 实例上,可是每一个实例都同步 queue 的元数据(元数据能够认为是 queue 的一些配置信息,经过元数据,能够找到 queue 所在实例)。你消费的时候,实际上若是链接到了另一个实例,那么那个实例会从 queue 所在实例上 拉取数据过来。这种方式确实很麻烦,也不怎么好,没作到所谓的分布式,就是个普通集群。由于这致使你要么消费者每次随机链接一个实例而后拉取数据,要么固定链接那个 queue 所在实 例消费数据,前者有数据拉取的开销,后者致使单实例性能瓶颈。并且若是那个放 queue 的实例宕机了,会致使接下来其余实例就没法从那个实例拉取,若是你开启了消息持久化,让 RabbitMQ 落地存储消息的话,消息不必定会丢,得等这个实例恢复了,而后才能够继续从这个 queue 拉取数据。
镜像集群模式的策略是高可用策略,指定的时候能够要求数据同步到全部节点的,也能够要求同步到指定数量的节点,再次建立 queue 的时候,应用这个策略,就会自动将数据同步到其余的 节点上去了。api

https://www.javazhiyin.com/22905.html缓存

五、如何解决消息队列的延时以及过时失效问题?
其实本质针对的场景,都是说,可能你的消费端出了问题,不消费了;或者消费的速度极其慢,形成消息堆积了,MQ存储快要爆了,甚至开始过时失效删除数据了。异步

针对这个问题能够有事前、事中、过后三种处理

  • 事前:开发预警程序,监控最大的可堆积消息数,超过就发预警消息(好比短信),不要等出生产事故了再处理。
  • 事中:看看消费端是否是故障中止了,紧急重启。
  • 过后:中华石杉老师就是说的这一种(https://github.com/doocs/advanced-java/blob/master/docs/high-concurrency/mq-time-delay-and-expired-failure.md),须要对消费端紧急扩容 ,增长处理消费者进程,如扩充10倍处理,但其实这也有个问题,即数据库的吞吐是有限制的,若是是消费到数据库也是没办法巨量扩容的,因此仍是要在吞吐能力支持下老老实实的泄洪消 费。因此事前预防仍是最重要的。不然出发删除过时数据,那就须要再重写生产消息的程序,从新产生消息。

六、RabbitMQ如何保证不丢数据?
须要考虑3个可能丢数据的地方:生产端、队列自己、消费端

  • 6.1生产端:开启事务(不推荐,太耗性能下降吞吐),推荐开启 confirm 模式,在生产者那里设置开启 confirm 模式以后,你每次写的消息都会分配一个惟一的 id,而后若是写入了RabbitMQ 中,RabbitMQ 会给你回传一个 ack 消息,告诉你说这个消息 ok 了。若是 RabbitMQ 没能处理这个消息,会回调你的一个 nack 接口,告诉你这个消息接收失败,你能够重试。而 且你能够结合这个机制本身在内存里维护每一个消息 id 的状态,若是超过必定时间还没接收到这个消息的回调,那么你能够重发。
  • 6.2队列自己:就是 RabbitMQ 本身弄丢了数据,这个你必须开启 RabbitMQ 的持久化,就是消息写入以后会持久化到磁盘,哪怕是 RabbitMQ 本身挂了,恢复以后会自动读取以前存储的数据,通常数据不会丢。

设置持久化有两个步骤:

    •  建立 queue 的时候将其设置为持久化,这样就能够保证 RabbitMQ 持久化 queue 的元数据,可是它是不会持久化 queue 里的数据的。
    •  第二个是发送消息的时候将消息的 deliveryMode 设置为 2。就是将消息设置为持久化的,此时 RabbitMQ 就会将消息持久化到磁盘上去。
  • 6.3消费端:其实和kafka的原理很相似,kafka即手动提交offsize。用RabbitMQ 提供的 ack 机制,简单来讲,就是你必须关闭 RabbitMQ 的自动 ack,经过本身的一个 api 来调用就行,而后每次你本身代码里确保处理完的时候,再在程序里 ack。这样的话,若是你还没处理完,不就没有 ack 了?那 RabbitMQ 就认为你还没处理完,这个时候 RabbitMQ 会把这个消费分配给别 的 consumer 去处理,消息是不会丢的。

七、如何保证队列的消息不被重复消费?
这个须要灵活做答,考察的是思考力,由于消费的场景有不少,有数据库、有缓存、有第三方接口

  • 1.好比针对数据库,你拿到这个消息作数据库的insert操做。那就容易了,给这个消息作一个惟一主键(或者UUID),那么就算出现重复消费的状况,就会致使主键冲突,避免数据库出现脏数据。
  • 2.再好比redis缓存,你拿到这个消息作redis的set的操做,那就容易了,不用解决,由于你不管set几回结果都是同样的,set操做原本就算幂等操做。
  • 3.再好比第三方接口,须要肯定两点,第三方接口程序是有去重能力的,那么脏一点直接丢数据过去,若是没有去重能力,仍是须要咱们来写程序去重,就是第2点的办法。

八、集群节点类型都有什么?
节点的存储类型分为两种:

  • 磁盘节点
  • 内存节点

磁盘节点就是配置信息和元信息存储在磁盘上,内存节点把这些信息存储在内存中,固然内次节点的性能是大大超越磁盘节点的。单节点系统必须是磁盘节点,不然每次你重启RabbitMQ以后全部的系统配置信息都会丢失。RabbitMQ要求集群中至少有一个磁盘节点,当节点加入和离开集群时,必须通知磁盘节点。

相关文章
相关标签/搜索