更多面试题:https://www.yuque.com/greedy-9i38g/tzpwui?# 《面试题》
目录
1、为啥使用消息队列哇?
2、消息队列有哪些优缺点呐?
3、Kafka、Active/Rabbit/Rocket MQ都有哪些区别哇?
4、如何确保消息队列的高可用哇?
5、咋保证消息被消费时的幂等性?
6、如何确保消息的可靠性传输?
7、如何保证消息的顺序性?
8、咋解决消息队列的延时以及过期失效问题?有几百万消息积压咋办?
9、如果让你写个消息队列,你咋设计?
消息队列使用场景有很多,但是其核心是三点:削峰、异步、解耦
解耦:想象一下,A系统发送数据到B、C、D系统,万一突然来业务E系统也需要数据,但是C系统又不需要数据了。B系统还因为负载挂掉了...
总不能因为每个系统自己的问题,频繁的改动太多的A系统吧?记住没有什么是加一层解决不了的,如果不行,那就再加一层
异步:想象一下,A系统接收一个请求,需要在自己本地写库,还需要在BCD三个系统写库,自己本地写库要3ms,BCD三个系统分别写库要300ms、450ms、200ms。最终请求总延时是3 + 300 + 450 + 200 = 953ms,接近1s,用户感觉搞个什么东西,慢死了慢死了。
削峰:每天0点~11点,A系统风平浪静,每秒请求数量100条,但是到了11点之后,请求量暴增到1万条,但是系统最大的处理能力就只能是每秒钟处理1000个请求啊...系统会死的...
我这里只讲了什么场景适合用消息队列,至于削峰、异步、解耦具体怎么用,大家自行百度吧,百度一大堆呐😁
优点上面说了:削峰、异步、解耦
缺点也不少
还有个,是我自己认为的,代码里加入MQ后之后,再找代码之间调用的时候,明显没有接口之间调用找的快了 ̄へ ̄
特性 | ActiveMQ | RabbitMQ | RocketMQ | Kafka |
单机吞吐量 | 万级,吞吐量比RocketMQ和Kafka要低了一个数量级 | 万级,吞吐量比RocketMQ和Kafka要低了一个数量级 | 10万级,RocketMQ也是可以支撑高吞吐的一种MQ | 10万级别,这是kafka最大的优点,就是吞吐量高。 一般配合大数据类的系统来进行实时数据计算、日志采集等场景 |
可用性 | 高,基于主从架构实现高可用性 | 高,基于主从架构实现高可用性 | 非常高,分布式架构 | 非常高,kafka是分布式的,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用 |
优劣势总结 | 我没用过,大家自行百度吧... | erlang语言开发,性能极其好,延时很低;社区活跃度高,好处是,你有问题大部分都可以找到答案。 缺点吧,想改源码不好改 | 背靠阿里,Java开发。而且有实际实战数据摆在那里,一个字:强 | kafka唯一的一点劣势是有可能消息重复消费,对数据准确性会造成极其轻微的影响 |
4.1、RabbitMQ高可用
Rabbit MQ是最具有代表性的,它是基于主从做的高可用的。它有三种模式:单机模式、普通集群、镜像集群
4.2、Kafka高可用
Kafka最基本的架构:多个Broker组成,每个Broker是一个节点。创建一个Topic,这个Topic可以划分为多个partition,每个partition可以存在于不同的Broker上,每个partition就放一部分数据。这跟Redis-cluster很像...
跟MQ相比的不同,这就是天然的分布式消息队列,一个Topic的数据,打散放在不同的机器(Broker)上,每个上面放一部分数据。MQ只能称为高可用
kafka 0.8以后,提供了HA机制,就是replica副本机制。每个partition的数据都会同步到其他机器上,形成自己的多个replica副本。然后所有replica会选举一个leader出来,那么生产和消费都跟这个leader打交道,然后其他replica就是follower。写的时候,leader会负责把数据同步到所有follower上去,读的时候就直接读leader上数据即可。只能读写leader?很简单,要是你可以随意读写每个follower,那么就要注意数据一致性的问题,系统复杂度太高,很容易出问题。Kafka会均匀的将一个partition的所有replica分布在不同的机器上,这样才可以提高容错性
这么搞,就有所谓的高可用性了,因为如果某个Broker宕机了,没事儿,那个Broker上面的partition在其他机器上都有副本的,如果这上面有某个partition的leader,那么此时会重新选举一个新的leader出来,大家继续读写那个新的leader即可。这就有所谓的高可用性了。
写数据的时候,生产者就写leader,然后leader将数据落地写本地磁盘,接着其他follower自己主动从leader来pull数据。一旦所有follower同步好数据了,就会发送ack给leader,leader收到所有follower的ack之后,就会返回写成功的消息给生产者。(当然,这只是其中一种模式,还可以适当调整这个行为)
消费的时候,只会从leader去读,但是只有一个消息已经被所有follower都同步成功返回ack的时候,这个消息才会被消费者读到
以Kafka为例,Kafka有个偏移量(offset)的概念,就是每个消息写进去,都会有个offset代表它的序号。消费者消费之后,会把当前消费成功的消息告诉Kafka,代表当前序号的消息我消费过了,下次消费者再拉取数据的时候,Kafka你要从我上次消费到的地方继续消费。这样确保了,即时Kafka重启了,消费者也会从宕机之前的位置继续拉取数据
但是还是会有少量的数据会被重复消费,因为保不齐消费者还没通知Kafka我已经消费过当前消息的时候呢,Kafka的机子重启了。这样还是会有一点点的数据会被重复消费,有人认为就那一点点的重复数据,无所谓,还有数据库主键撑着呢。但是你能确保所有数据都是跟数据库操作的吗?万一插入的数据表没有主键呢?那不G了....
啥时幂等性?就是一个数据或者请求,不论你重复多少次,数据都是不会变得,就像电商的下单按钮,你要是网不好,咔咔咔点好几下,下好几单....
具体业务可以有不同的处理方式:
丢数据,就两种。队列把消息整丢了,或者代码里整丢了
6.1、Rabbit MQ
1、生产者弄丢数据
生产者把消费发送到MQ的时候,可能因为网络原因给整丢了
这俩模式的区别,显而易见一个是同步阻塞,一个是异步
2、MQ弄丢数据
这个必须开启MQ的持久化功能,就是消息写入MQ之后会持久化到硬盘上,哪怕是MQ挂了,重启后也能找回数据。但是也有极限,那就是还没持久化呢,MQ挂了...
设置持久化有两个步骤,第一创建queue的时候将其设为持久化的,这样就可以保证MQ持久化queue的元数据,但是不会持久化queue里面的数据。第二是发送消息的时候,将消息持久化。想要将消息持久化必须同时开启这俩功能
而且持久化可以与生产者那边联合起来,只有消息被持久化到磁盘后,才回调ack给生产者
3、消费者弄丢数据
MQ如果丢失数据,可能是正在消费的时候,进程挂了,比如重启了...
这时候还可以用MQ的ack机制,简单说就是关闭MQ自动ack,通过代码里面调用API来完成。当你消费者把这条消息的业务处理完了,在代码里面手动ack。这样的话你如果迟迟没有ack,MQ就把这个消息分配给其他消费者进行处理
6.2、Kafka
1、消费者弄丢数据
关闭Kafka自动提交offset,在代码中处理完消息后手动提交offset,这样就可以确保数据不会丢
2、Kafka弄丢数据
比较常见的场景,Kafka某个Broker宕机,在重新选举partition的leader的时候,恰逢此时此刻leader正在给follower同步数据的时候。有一部分数据就丢了
所以要给Kafka设置至少以下4个参数
3、生产者会不会弄丢数据
如果按照上述的思路设置了ack=all,一定不会丢,要求是,你的leader接收到消息,所有的follower都同步到了消息之后,才认为本次写成功了。如果没满足这个条件,生产者会自动不断的重试,重试无限次
你搁MySQL里增删改一条数据,对应出来3条binlog,接着3条binlog发送到MQ里面。本来消费端的消费顺序是:增加、修改、删除。结果给换了消费顺序成:删除、增加、修改...
会导致消费顺序错乱的场景:
如何保证消息的顺序性?
8.1、Kafka大量消息堆积在MQ里,好几个小时还没解决
这事儿我还真遇见过,当时就在发布生产之前就考虑到这种场景了,结果生产上一看,按目前的逻辑得处理18天...紧急放开开关,让代码走另一套逻辑
出现这种情况,如果没有提前想好对应策略,只能做紧急扩容
这种做法就是人为的把队列资源和消费者资源扩大10倍,等消息积压处理的差不多了,再恢复原架构
8.2、Rabbit MQ
如果是Rabbit MQ,MQ是可以设置过期时间的,如果消息在MQ里积压超过一定时间,就会被MQ给清理掉。这可就不是积压的问题了,是数据没了啊!!!
这种情况下,就不是说增加消费者可以解决的了,等你写完程序发上去,消息都没了。咋解决呢?只能人为的半夜,写程序把丢失的数据找回来,重新塞MQ里
如果面试官真问了,就按Kafka的设计理念回答