目录:
你使用过消息中间件吗?
你们为什么使用消息中间件?
消息队列有什么好处?
什么场景下使用的?用来干了个什么的事情?
系统中引入消息队列之后会不会有什么坏处?
你们使用哪一个消息中间件?为什么选这一种?
kafka、activemq、rabbitmq、rocketmq都有什么优点和缺点啊?
消息中间件还可以做什么其他的事情?
你们是如何保证消息队列的高可用的呀?
你们怎么保证消息不被重复消费呀?
如何保证消费的时候是幂等的?
如何保证消息的可靠传输啊?要是消息丢了怎么办
如何保证消息的顺序性啊?
消息队列满了怎么处理?
如何解决消息有很多消息,如果挤压了几百万条消息,积压了几个小时,这个时候消息队列的延时和过期失效怎么解决呀?
如果让你写一个消息队列,该如何进行架构设计啊?说一下你的思路?
其实面试官主要是想看看:
第一,你知道不知道你们系统里为什么要用消息队列这个东西?
我之前面试就见过大量的候选人,说自己项目里用了redis、mq,但是其实他并不知道自己为什么要用这个东西。其实说白了,就是为了用而用,或者是别人设计的架构,他从头到尾没思考过。
没有对自己的架构问过为什么的人,一定是平时没有思考的人,面试官对这类候选人印象通常很不好。因为进了团队担心你就木头木脑的干呆活儿,不会自己思考。
第二,你既然用了消息队列这个东西,你知道不知道用了有什么好处?
系统中引入消息队列之后会不会有什么坏处?你要是没考虑过这个,那你盲目弄个MQ进系统里,后面出了问题你是不是就自己溜了给公司留坑?你要是没考虑过引入一个技术可能存在的弊端和风险,面试官把这类候选人招进来了,基本可能就是挖坑型选手。就怕你干1年挖一堆坑,自己跳槽了,给公司留下后患无穷
第三,既然你用了MQ,可能是某一种MQ,那么你当时做没做过调研啊?
你别傻乎乎的自己拍脑袋看个人喜好就瞎用了一个MQ,比如kafka。甚至都从没调研过业界到底流行的MQ有哪几种?每一个MQ的优点和缺点是什么?每一个MQ没有绝对的好坏,但是就是看用在哪个场景可以扬长避短,利用其优势,规避其劣势。
如果是一个不考虑技术选型的候选人招进了团队,面试官交给他一个任务,去设计个什么系统,他在里面用一些技术,可能都没考虑过选型,最后选的技术可能并不一定合适,一样是留坑
这两个问题其实是一样的,消息队列主要优点是可以实现:解耦、异步、削峰
其实就是问问你消息队列都有哪些使用场景,然后你项目里具体是什么场景,说说你在这个场景里用消息队列是什么,期望的一个回答是说,你们公司有个什么业务场景,这个业务场景有个什么技术挑战,如果不用MQ可能会很麻烦,但是你现在用了MQ之后带给了你很多的好处 回答:我们从三个核心的场景来分析一下:解耦、异步、削峰
1)解耦:现场画个图来说明一下,A系统发送个数据到BCD三个系统,接口调用发送
那如果E系统也要这个数据呢?那如果C系统现在不需要了呢?现在A系统又要发送第二种数据了呢?A系统负责人濒临崩溃中。。。再来点更加崩溃的事儿,A系统要时时刻刻考虑BCDE四个系统如果挂了咋办?我要不要重发?我要不要把消息存起来?头发都白了啊。。。面试技巧:一个大规模系统,往往会拆分为几十个甚至上百个子系统,每个子系统又对应N多个服务,这些系统与系统之间有着错综复杂的关系网络。
如果某个系统产出一份核心数据,可能下游无数的其他系统都需要这份数据来实现各种业务逻辑。此时如果你要是采取上面那种模式来设计系统架构,那么绝对你负责系统A的同学要被烦死了。先是来一个人找他要求发送数据给一个新的系统H,系统A的同学要修改代码然后在那个代码里加入调用新系统H的流程。一会那个系统B是个陈旧老系统要下线了,告诉系统A的同学:别给我发送数据了,接着系统A再次修改代码不再给这个系统B。然后如果要是某个下游系统突然宕机了呢?系统A的调用代码里是不是会抛异常?那系统A的同学会收到报警说异常了,结果他还要去care是下游哪个系统宕机了。所以在实际的系统架构设计中,如果全部采取这种系统耦合的方式,在某些场景下绝对是不合适的,系统耦合度太严重。并且互相耦合起来并不是核心链路的调用,而是一些非核心的场景(比如上述的数据消费)导致了系统耦合,这样会严重的影响上下游系统的开发和维护效率。因此在上述系统架构中,就可以采用MQ中间件来实现系统解耦。系统A就把自己的一份核心数据发到MQ里,下游哪个系统感兴趣自己去消费即可,不需要了就取消数据的消费,如下图所示。
2)异步:假设你有一个系统调用链路,是系统A调用系统B,一般耗时20ms;系统B调用系统C,一般耗时200ms;系统C调用系统D,一般耗时2s, 现场画个图来说明一下,A系统接收一个请求,需要在自己本地写库,还需要在BCD三个系统写库,自己本地写库要3ms,BCD三个系统分别写库要300ms、450ms、200ms。最终请求总延时是32+ 200 + 2000 = 2232ms,接近3S,用户感觉搞个什么东西,慢死了慢死了。但是实际上,链路中的系统A调用系统B,系统B调用系统C,这两个步骤起来也就220ms。
就因为引入了系统C调用系统D这个步骤,导致最终链路执行时间是2秒多,直接将链路调用性能降低了10倍,这就是导致链路执行过慢的罪魁祸首。
那此时我们可以思考一下,是不是可以将系统D从链路中抽离出去做成异步调用呢?其实很多的业务场景是可以允许异步调用的。
你平时点个外卖,咔嚓一下子下订单然后付款了,此时账户扣款、创建订单、通知商家给你准备菜品。
接着,是不是需要找个骑手给你送餐?那这个找骑手的过程,是需要一套复杂算法来实现调度的,比较耗时。但是其实稍微晚个几十秒完成骑手的调度都是ok的,因为实际并不需要在你支付的一瞬间立马给你找好骑手,也没那个必要。
那么我们是不是就可以把找骑手给你送餐的这个步骤从链路中抽离出去,做成异步化的,哪怕延迟个几十秒,但是只要在一定时间范围内给你找到一个骑手去送餐就可以了。这样是不是就可以让你下订单点外卖的速度变得超快?支付成功之后,直接创建好订单、账户扣款、通知商家立马给你准备做菜就ok了,这个过程可能就几百毫秒。然后后台异步化的耗费可能几十秒通过调度算法给你找到一个骑手去送餐,但是这个步骤不影响我们快速下订单。当然我们不是说那些大家熟悉的外卖平台的技术架构就一定是这么实现的,只不过是用一个生活中常见的例子给大家举例说明而已。
所以上面的链路也是同理,如果业务流程支持异步化的话,是不是就可以考虑把系统C对系统D的调用抽离出去做成异步化的,不要放在链路中同步依次调用。
这样,实现思路就是系统A -> 系统B -> 系统C,直接就耗费220ms后直接成功了。
然后系统C就是发送个消息到MQ中间件里,由系统D消费到消息之后慢慢的异步来执行这个耗时2s的业务处理。通过这种方式直接将核心链路的执行性能提升了10倍。
3)削峰:假设你有一个系统,有100万的用户,平时正常的时候每秒可能就几百个请求,系统部署在8核16G的机器的上,正常处理都是ok的,每秒几百请求是可以轻松抗住的。但是每次一到11点~1点,每秒并发请求数量突然会暴增到5000条,这个时候就有5000个请求写入到mq,但是系统最大的处理能力就只能是每秒钟处理2000个请求啊,因为mysql能处理的请求大概是2000个,这就尴尬了,系统会死。。。
在高峰期一下子来了每秒钟几千请求,瞬时出现了流量高峰,此时你会怎么做呢?
那如果瞬时高峰每天就那么半个小时,接着直接就降低为了每秒就几百请求,如果你线上部署了很多台机器,那么每台机器就处理每秒几十个请求就可以了,这不是有点浪费机器资源吗?大部分时候,每秒几百请求,一台机器就足够了,但是为了抗那每天瞬时的高峰,硬是部署了10台机器,每天就那半个小时有用,别的时候都是浪费资源的,所以这种方案不建议使用
使用mq来进行削峰
此时我们就可以用MQ中间件来进行流量削峰。所有机器前面部署一层MQ,系统从mq中拉取请求,妹秒就拉取2000个请求,不要超过能处理的最大的请求数量就可以了,这样子做可以保证哪怕是在高峰的时候,系统绝对不会挂。如果每一秒有5000个请求进来,结果就2000个请求出去,结果就回导致在高峰期积累几十万甚至是几百万的请求积累在mq中,这个短暂的高峰期的积累是ok的,因为高峰期过了之后,每秒钟就50个请求进来mq了,但是系统还是会按照每秒钟2000个请求的速度在处理,所以说,只要高峰期一过,系统就回快速的将积压的消息给解决掉。
这个就是很典型的一个MQ的用法,用有限的机器资源承载高并发请求,如果业务场景允许异步削峰,高峰期积压一些请求在MQ里,然后高峰期过了,后台系统在一定时间内消费完毕不再积压的话,那就很适合用这种技术方案。
优点上面已经说了,就是在特殊场景下有其对应的好处,解耦、异步、削峰
缺点呢?显而易见的
1)系统可用性降低:系统引入的外部依赖越多,越容易挂掉,本来你就是A系统调用BCD三个系统的接口就好了,人ABCD四个系统好好的,没啥问题,你偏加个MQ进来,万一MQ挂了咋整?MQ挂了,整套系统崩溃了,你不就完了么。
2)系统复杂性提高:硬生生加个MQ进来,你怎么保证消息没有重复消费?怎么处理消息丢失的情况?怎么保证消息传递的顺序性?头大头大,问题一大堆,痛苦不已
3)一致性问题:A系统处理完了直接返回成功了,人都以为你这个请求就成功了;但是问题是,要是BCD三个系统那里,BD两个系统写库成功了,结果C系统写库失败了,咋整?你这数据就不一致了。
所以消息队列实际是一种非常复杂的架构,你引入它有很多好处,但是也得针对它带来的坏处做各种额外的技术方案和架构来规避掉,最好之后,你会发现,妈呀,系统复杂度提升了一个数量级,也许是复杂了10倍。但是关键时刻,用,还是得用的。。。
常见的MQ其实就这几种,别的还有很多其他MQ,但是比较冷门的。
这个首先你可以说下你们公司选用的是什么消息中间件,比如用的是RabbitMQ,然后可以初步给一些你对不同MQ中间件技术的选型分析。
1)activemq:比如说ActiveMQ是老牌的消息中间件,国内很多公司过去运用的还是非常广泛的,功能很强大,使用较多的是一些传统企业,用ActiveMQ做异步调用和系统解耦,
但是问题在于没法确认ActiveMQ可以支撑互联网公司的高并发、高负载以及高吞吐的复杂场景,社区也不是很活跃,在国内互联网公司落地较少。所以大家还是算了吧,我个人不推荐用这个了。
2)RabbitMQ:他的好处在于可以支撑高并发、高吞吐、性能很高,同时有非常完善便捷的后台管理界面可以使用。
另外,他还支持集群化、高可用部署架构、消息高可靠支持,功能较为完善。
而且经过调研,国内各大互联网公司落地大规模RabbitMQ集群支撑自身业务的case较多,国内各种中小型互联网公司使用RabbitMQ的实践也比较多。
除此之外,RabbitMQ是活跃开源社区,绝对不会黄,较高频率的迭代版本,来修复发现的bug以及进行各种优化,因此综合考虑过后,公司采取了RabbitMQ。
但是RabbitMQ也有一点缺陷,就是他自身是基于erlang语言开发的,所以导致较为难以分析里面的源码,也较难进行深层次的源码定制和改造,毕竟需要较为扎实的erlang语言功底才可以。
3)RocketMQ:现在确实越来越多的公司,会去用RocketMQ,确实很不错,阿里开源的,经过阿里的生产环境的超高并发、高吞吐的考验,性能卓越,同时还支持分布式事务等特殊场景,但是我提醒一下自己想好社区万一突然黄掉的风险,对自己公司技术实力有绝对自信的,我推荐用RocketMQ,因为RocketMQ是基于Java语言开发的,适合深入阅读源码,有需要可以站在源码层面解决线上生产问题,包括源码的二次开发和改造。所以中小型公司,技术实力较为一般,技术挑战不是特别高,用RabbitMQ是不错的选择;大型公司,基础架构研发实力较强,用RocketMQ是很好的选择
4)Kafka:Kafka提供的消息中间件的功能明显较少一些,相对上述几款MQ中间件要少很多。
但是Kafka的优势在于专为超高吞吐量的实时日志采集、实时数据同步、实时数据计算等场景来设计。
因此Kafka在大数据领域中配合实时计算技术(比如Spark Streaming、Storm、Flink)使用的较多。但是在传统的MQ中间件使用场景中较少采用。
综合比较示意图:
往期精选
面试题:46道经典Linux面试题(含答案)
面试题:你不可不知的HashMap面试连环炮
面试题:InnoDB中一棵B+树能存多少行数据?
面试题:面试官问你MySQL的优化,看这篇文章就够了
面试题:在浏览器输入 URL 回车之后发生了什么?(超详细版)
面试题:讲讲Redis有哪几个过期策略?
工具篇:这可能是全 GitHub 最牛逼的抢票神器!
码农进阶之路
长按二维码关注
面经 | 原理 | 源码 | 实战 | 工具