消息队列⾯试题及答案
1、为什么使⽤消息队列?
消息队列使⽤的场景和中间件有很多,但解决的核⼼问题主要是:异步、解耦、消峰填⾕。
2、消息队列的优缺点
异步、解耦、消峰填⾕这是消息队列最⼤的优点,除了这些消息队列还可以会解决⼀些我们特殊业务场景的问题。但是缺点主要在于系统的可⽤性、复杂性、⼀致性问题,引⼊消息队列后,需要考虑MQ的可⽤性,万⼀MQ崩溃了岂不是要爆炸?⽽且复杂性明显提⾼了,需要考虑⼀些消息队列的常见问题和解决⽅案,还有就是⼀致性问题,⼀条消息由多个消费者消费,万⼀有⼀个消费者消费失败了,就会导致数据不⼀致。
3、如何保证消息队列的⾼可⽤?
由于笔者只使⽤和实践过RabbitMQ和Kafka,RocketMQ和ActiveMQ了解的不深,所以分析⼀下RabbitMQ和Kafka的⾼可⽤。
(⼀)RabbitMQ
RabbitMQ有三种模式:单机模式,普通集模式,镜像集模式
(1)单机模式
单机模式平常使⽤在开发或者本地测试场景,⼀般就是测试是不是能够正确的处理消息,⽣产上基本没⼈去⽤单机模式,风险很⼤。
(2)普通集模式
普通集模式就是启动多个RabbitMQ实例。在你创建的queue,只会放在⼀个rabbtimq实例上,但是每个实例都同步queue的元数据。在消费的时候完了,上如果连接到了另外⼀个实例,那么那个实例会从queue所在实例上拉取数据过来。
这种⽅式确实很⿇烦,也不怎么好,没做到所谓的分布式,就是个普通集。因为这导致你要么消费者每次随机连接⼀个实例然后拉取数据,要么固定连接那个queue所在实例消费数据,前者有数据拉取的开销,后者导致单实例性能瓶颈。
⽽且如果那个放queue的实例宕机了,会导致接下来其他实例就⽆法从那个实例拉取,如果你开启了消息持久化,让RabbitMQ落地存储消息的话,消息不⼀定会丢,得等这个实例恢复了,然后才可以继续从这个queue拉取数据。
这⽅案主要是提⾼吞吐量的,就是说让集中多个节点来服务某个queue的读写操作。
(3)镜像集模式
镜像集模式是所谓的RabbitMQ的⾼可⽤模式,跟普通集模式不⼀样的是,你创建的queue,⽆论元数据还是queue⾥的消息都会存在于多个实例上,然后每次你写消息到queue的时候,都会⾃动把消息到多个实例的queue⾥进⾏消息同步。
优点在于你任何⼀个实例宕机了,没事⼉,别的实例都可以⽤。缺点在于性能开销太⼤和扩展性很低,同步所有实例,这会导致⽹络带宽和压⼒很重,⽽且扩展性很低,每增加⼀个实例都会去包含已有的queue的所有数据,并没有办法线性扩展queue。
activemq默认账号密码开启镜像集模式可以去RabbitMQ的管理控制台去增加⼀个策略,指定要求数据同步到所有节点的,也可以要求就同步到指定数量的节点,然后你再次创建queue的时候,应⽤这个策略,就会⾃动将数据同步到其他的节点上去了。
(⼆)Kafka
Kafka天⽣就是⼀个分布式的消息队列,它可以由多个broker组成,每个broker是⼀个节点;你创建⼀个topic,这个topic可以划分为多个partition,每个partition可以存在于不同的broker上,每个partition就放
⼀部分数据。
kafka 0.8以前,是没有HA机制的,就是任何⼀个broker宕机了,那个broker上的partition就废了,没法写也没法读,没有什么⾼可⽤性可⾔。
kafka 0.8以后,提供了HA机制,就是replica副本机制。kafka会均匀的将⼀个partition的所有replica分布在不同的机器上,来提⾼容错性。每个partition的数据都会同步到吉他机器上,形成⾃⼰的多个replica副本。然后所有replica会选举⼀个leader出来,那么⽣产和消费都去leader,其他replica就是follower,leader会同步数据给follower。当leader挂了会⾃动去replica,然后会再选举⼀个leader出来,这样就具有⾼可⽤性了。
写数据的时候,⽣产者就写leader,然后leader将数据落地写本地磁盘,接着其他follower⾃⼰主动从leader来pull数据。⼀旦所有follower同步好数据了,就会发送ack给leader,leader收到所有follower的ack之后,就会返回写成功的消息给⽣产者。(当然,这只是其中⼀种模式,还可以适当调整这个⾏为)
消费的时候,只会从leader去读,但是只有⼀个消息已经被所有follower都同步成功返回ack的时候,这个消息才会被消费者读到。
4、如何保证消息不被重复消费?或者说,如何保证消息消费的幂等性?
其实消息重复消费的主要原因在于回馈机制(RabbitMQ是ack,Kafka是offset),在某些场景中我们采⽤的回馈机制不同,原因也不同,例如消费者消费完消息后回复ack, 但是刚消费完还没来得及提交系统就重启了,这时候上来就pull消息的时候由于没有提交ack或者offset,消费的还是上条消息。
那么如何怎么来保证消息消费的幂等性呢?实际上我们只要保证多条相同的数据过来的时候只处理⼀条或者说多条处理和处理⼀条造成的结果相同即可,但是具体怎么做要根据业务需求来定,例如⼊库消息,先查⼀下消息是否已经⼊库啊或者说搞个唯⼀约束啊什么的,还有⼀些
是天⽣保证幂等性就根本不⽤去管,例如redis就是天然幂等性。
还有⼀个问题,消费者消费消息的时候在某些场景下要放过消费不了的消息,遇到消费不了的消息通过⽇志记录⼀下或者搞个什么措施以后再来处理,但是⼀定要放过消息,因为在某些场景下例如spring-rabbitmq的默认回馈策略是出现异常就没有提交ack,导致了⼀直在重发那条消费异常的消息,⽽且⼀直还消费不了,这就尴尬了,后果你会懂的。
5、如何保证消息的可靠性传输?或者说,如何处理消息丢失的问题?
参考:www.jianshu/p/06e7e3b34dd6
6、如何保证消息的顺序性?
因为在某些情况下我们扔进MQ中的消息是要严格保证顺序的,尤其涉及到订单什么的业务需求,消费的时候也是要严格保证顺序,不然会出⼤问题的。
先看看顺序会错乱的两个场景:
rabbitmq:⼀个queue,多个consumer,这不明显乱了
kafka:⼀个topic,⼀个partition,⼀个consumer,内部多线程,这不也明显乱了
7、如何解决消息队列的延时以及过期失效问题?消息队列满了以后该怎么处理?有⼏百万消息持续积压⼏⼩时怎么解决?
(⼀)、⼤量消息在mq⾥积压了⼏个⼩时了还没解决
⼏千万条数据在MQ⾥积压了七⼋个⼩时,从下午4点多,积压到了晚上很晚,10点多,11点多
这个是我们真实遇到过的⼀个场景,确实是线上故障了,这个时候要不然就是修复consumer的问题,让他恢复消费速度,然后傻傻的等待⼏个⼩时消费完毕。这个肯定不能在⾯试的时候说吧。
⼀个消费者⼀秒是1000条,⼀秒3个消费者是3000条,⼀分钟是18万条,1000多万条,所以如果你积压了⼏百万到上千万的数据,即使消费者恢复了,也需要⼤概1⼩时的时间才能恢复过来。
⼀般这个时候,只能操作临时紧急扩容了,具体操作步骤和思路如下:
先修复consumer的问题,确保其恢复消费速度,然后将现有cnosumer都停掉。
新建⼀个topic,partition是原来的10倍,临时建⽴好原先10倍或者20倍的queue数量。
然后写⼀个临时的分发数据的consumer程序,这个程序部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写⼊临时建⽴好的10倍数量的queue。
接着临时征⽤10倍的机器来部署consumer,每⼀批consumer消费⼀个临时queue的数据。
这种做法相当于是临时将queue资源和consumer资源扩⼤10倍,以正常的10倍速度来消费数据。
等快速消费完积压数据之后,得恢复原先部署架构,重新⽤原先的consumer机器来消费消息。
(⼆)、消息队列过期失效问题
假设你⽤的是rabbitmq,rabbitmq是可以设置过期时间的,就是TTL,如果消息在queue中积压超过⼀定的时间就会被rabbitmq给清理掉,这个数据就没了。那这就是第⼆个坑了。这就不是说数据会⼤量积压在mq⾥,⽽是⼤量的数据会直接搞丢。
这个情况下,就不是说要增加consumer消费积压的消息,因为实际上没啥积压,⽽是丢了⼤量的消息。我们可以采取⼀个⽅案,就是批量重导,这个我们之前线上也有类似的场景⼲过。就是⼤量积压的时候,我们当时就直接丢弃数据了,然后等过了⾼峰期以后,⽐如⼤家⼀起喝咖啡熬夜到晚上12点以后,⽤户都睡觉了。
这个时候我们就开始写程序,将丢失的那批数据,写个临时程序,⼀点⼀点的查出来,然后重新灌⼊mq⾥⾯去,把⽩天丢的数据给他补回来。也只能是这样了。
假设1万个订单积压在mq⾥⾯,没有处理,其中1000个订单都丢了,你只能⼿动写程序把那1000个订单给查出来,⼿动发到mq⾥去再补⼀次。
(三)、消息队列满了怎么搞?
如果⾛的⽅式是消息积压在mq⾥,那么如果你很长时间都没处理掉,此时导致mq都快写满了,咋办?这个还有别的办法吗?没有,谁让你第⼀个⽅案执⾏的太慢了,你临时写程序,接⼊数据来消费,消费⼀个丢弃⼀个,都不要了,快速消费掉所有的消息。然后⾛第⼆个⽅案,到了晚上再补数据吧。
9、RabbitMQ 有哪些重要的⾓⾊?
10、RabbitMQ 有哪些重要的组件?
11、RabbitMQ 有⼏种⼴播类型?
三种⼴播模式:
fanout: 所有bind到此exchange的queue都可以接收消息(纯⼴播,绑定到RabbitMQ的接受者都能收到消息);
direct: 通过routingKey和exchange决定的那个唯⼀的queue可以接收消息;
topic: 所有符合routingKey(此时可以是⼀个表达式)的routingKey所bind的queue可以接收消息;
12、Kafka 可以脱离 zookeeper 单独使⽤吗?为什么?
kafka 不能脱离 zookeeper 单独使⽤,因为 kafka 使⽤ zookeeper 管理和协调 kafka 的节点服务器。
13、Kafka 有⼏种数据保留的策略?
kafka 有两种数据保存策略:
按照过期时间保留
按照存储的消息⼤⼩保留
14、Kafka 的分区策略有哪些?
给定了分区号,直接将数据发送到指定的分区⾥⾯去
没有给定分区号,给定数据的key值,通过key取上hashCode进⾏分区既没有给定分区号,也没有给定key值,直接轮循进⾏分区
⾃定义分区
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论