《我们一起面大厂》之RabbitMQ你问我答

398 阅读13分钟

《我们一起面大厂》之RabbitMQ你问我答文章中,由于小汪的技术焦虑感,引出了“大榜面试小汪”这个小故事,通过RabbitMQ你问我答,希望读者对RabbitMQ的核心概念、常见的消息模型、如何保证消息的可靠性、如何保证消息不会被重复消费、消息的顺序性消费等知识块有一个清晰的了解,搭建RabbitMQ学习的框架,同时更是RabbitMQ面试的重点考察点。希望能帮到正在学习RabbitMQ的你们,即将面试的你们!

1、技术的焦虑感

小汪在公司快2年了,刚去公司那会儿是很激动的,能将自己学到的Java知识运用到实际软件开发问题中,经过快2年的磨练,crud也逐渐熟练起来。只是涉及到3张表的业务之间的关联查询时,就摸不着头脑了,每到此时,小汪就叹气:哎,公司业务太复杂了。他心想:公司的项目使用Spring+Hibernate,之前的架构师已经把整个框架搭建好了,配置文件是采用上学期间学习的xml文件配置方式,现在外面都是SpringBoot项目,感觉这里的技术落后了,心里很焦虑。思来想去,小汪决定去找大榜排忧解难。

大榜:小汪,好长时间没见你了,也没发朋友圈的动态,最近忙啥呢?

小汪:我感觉自己产生了对技术的焦虑感。

大榜:哈哈哈,“技术的焦虑感”,这个词用得好,我有点好奇,你说的技术,指什么呢?

小汪:是这样的,我这几天总结了来公司两年以来的工作,虽然也学到了一些东西,但软件开发技术成长很慢。而且如果想在这家公司长久立足下去,必须要深入理解业务。我有时候观察那些在公司待了5年多的同事,技术不咋地,但人家对业务就是熟悉,在架构师搭的框架基础之上开发,工作得心应手,早早下班回家带小孩。我们刚来一两年的同事,因为对业务不熟,对稍微复杂的业务,就需要加班加点去完成。

大榜:听你这么一说,要是能把这些老员工都熬走,估计你就是公司最懂业务的员工了,到时候领导必须器重你小子。

小汪:哈哈哈,是啊。要是我熬到那个时候,也应该35岁了,技术虽然没啥成长,但这个行业的业务应该是摸得透透的,那时候也能在这个业务领域混口饭吃。

大榜:是啊,咱们程序员嘛,如果技术实力不咋地,但我们要是做到深入理解某个领域的业务,同样也可以闯出一片天地。

小汪:你说的没问题。但有时候我在想,感觉自己还年轻,不想一下子在这个业务领域搞10多年的开发,我还是想多学新技术,扩充自己的技术栈。上个月,咱们一起探讨了RabbitMQ消息中间件【SpringBoot与RabbitMQ整合,发送和接收消息实战(代码可运行)】,我下班后还一直在啃RabbitMQ的相关技术。

大榜:小伙子,不错啊,利用业务时间来充电,拥有优点程序员的好习惯。

小汪:榜哥,谬赞了。要不这样把,你考察下我对于RabbitMQ的理解,检验下这一个多月的学习成果。你作为面试官,我作为求职者,咱们开始唠会嗑吧。

唠嗑.gif

2、RabbitMQ你问我答

大榜:候选人,你好,我是xxx公司的高级软件开发攻城狮,欢迎你来参加面试。先来个自我介绍吧?

小汪:榜哥有演员的天赋啊,角色转换神速啊!小弟佩服!不过咱们都是十多年的老朋友了,自我介绍就不用了吧,要不咱们直接聊聊RabbitMQ,测试一下我对RabbitMQ的理解有多少?

大榜:哈哈哈,想临时充当下面试官,过把瘾。我看你的“简历”上,写了熟悉RabbitMQ消息中间件,而且有消息中间件的开发经验。能说说RabbitMQ的概念?

小汪:RabbitMQ是一款开源的,服务端使用Erlang语言编写,基于高级消息协议AMQP的消息中间件。

2.1、RabbitMQ与其他消息中间件的区别

大榜:你所在的公司为什么使用RabbitMQ作为消息中间件呢?使用其他的消息中间件可不可以?

小汪:面试官,是这样的,当时使用消息中间件时,是经过了一段时间的技术选型,通过对比了ActiveMQ、RabbitMQ、RocketMQ、Kafka这4种消息中间件的特点,最终选择了使用RabbitMQ作为公司的消息中间件。公司使用RabbitMQ的原因:主要是做多个业务模块之间的解耦、异步通信,从而提高系统的响应时间。

对于使用其他的消息中间件是否可行这个问题?也就是RabbitMQ与其他消息队列的联系和区别,业界目前主要使用ActiveMQ、RabbitMQ、RocketMQ、Kafka。首先,Kafka的业务场景主要大数据方向的日志采集功能,由于公司的这一块业务不是日志采集,所以没有使用Kafka。

对于ActiveMQ,是早期的项目会使用ActiveMQ,但吞吐量相对于RabbitMQ会比较低,而且社区不活跃。

对于RocketMQ,是阿里巴巴开源,目前阿里没有维护了,一般大型软件公司(如字节跳动)会在开源的基础上做定制化开发,要求公司的基础架构研发实力较强。

对于RabbitMQ,吞吐量、可用性都不错,而且客户端界面简单且实用,社区十分活跃,遇到问题,可以依靠开源社区的力量解决。鉴于公司的研发实力与大型软件公司存在差距,综合考虑后,选择了使用RabbitMQ作为消费中间件。所以说,使用其他消息中间件是可以的,但RabbbitMQ相比于其他消息中间件,更容易维护。

大榜:与其他消息中间件的对比,回答得很好,看来你们公司当时使用RabbitMQ是经过了慎重考虑。那RabbitMQ作为消息中间件,有什么优点、缺点呢?能列举一个场景说明下吗?

小汪:RabbitMQ的优点主要有削峰、解耦、异步通信。

具体的场景的话:秒杀活动,当所有用户同一时间点击某个商品抢购,产生巨大的用户流量,我们引入RabbitMQ后,用户的抢单请求排队入RabbitMQ队列,这样就间接帮助抢单接口进行了限流/削峰。

如“用户登录成功记录日志”的整个业务流程,“用户登录”是主流程,“记录用户登录轨迹”是辅助流程。我们可以通过RabbitMQ来实现这两个业务之间的解耦,并实现异步通信。当“用户登录”成功后,会通过RabbitMQ发送一条消息给队列,然后“记录用户登录轨迹”模块作为消费者,消费该条消息,实现业务之间的解耦、异步通信。优点大致是这3个。

2.2、面试官的补充提醒

大榜:嗯嗯,但RabbitMQ还有一种很重要的优点:业务延迟处理。能说下业务延迟处理的具体场景吗?

小汪:谢谢面试官的补充提醒。对于这种“需要延迟一定的时间后再进行处理”的业务,比如电商中抢购到商品后进行支付,若支付超时,则系统取消该订单。RabbitMQ可以通过设置TTL时间对这种延迟业务进行处理。

RabbitMQ的缺点主要有下面3点:系统的可用性降低;系统复杂性加大;一致性问题。

对于系统可用性降低:因为多引入了一个中间件,若该中间件异常,会导致系统无法正常运行。

系统复杂性加大:引入RabbitMQ后,我们需要去处理消息丢失、消息被重复消费,还有消息的顺序性问题。

一致性问题:举个栗子,A 系统处理完了直接返回成功了,以为这个请求就成功了;但是实际情况可能是: B、C、D 三个系统那里,B、D 两个系统写库成功了,结果 C 系统写库失败了,这就造成了数据的不一致。

【面试官视角:这个求职者对RabbitMQ的优点能准确的说出具体的使用场景;而且,对于引入RabbitMQ所带来的缺点,也能清楚地表述。这小伙子对RabbitMQ理解得不错,加分。】

悠闲.gif

2.3、RabbitMQ的核心概念、常见的消息模型

大榜:对RabbitMQ理解得还挺全面,私下没少花功夫啊。那我们再往下探索,RabbitMQ的几个核心概念?

小汪:核心概念,您这边指的是生产者、消息、消息模型、消费者吗?小汪憋出一张图:

消息模型.png

大榜:嗯嗯。消息模型,有那几种消息模型?它们的使用场景以及如何使用?

小汪:消息模型是由交换机、路由、队列组成。我们常用的主要有3种消息模型,基于FanoutExchange、DirectExchange、TopicExchange这三种消息模型。

对于FanoutExchange消息模型,应用场景为业务数据需要以广播方式传播的场景,比如“用户操作写到多个日志系统”。使用该消息模型,我们创建FanoutExchange交换机,并将队列与该交换机绑定起来。

对于DirectExchange消息模型,应用场景为业务数据需要直接传输并被消费的场景。

对于TopicExchange消息模型,TopicExchange也是RabbitMQ交换机中的一种,是一种“发布-主题-订阅”式的交换机。该交换机中,路由名称是支持“*”和“#”两种通配符,用于模糊匹配。“*”对应于一个单词,而“#”对应于0个或者多个单词。小汪再次憋出一张关于topic交换机的图:

image-20210509231335869.png

2.4、如何保证RabbitMQ消息的可靠传输?

大榜:嗯嗯。既然你们公司引入了RabbitMQ作为消息中间件,你们是怎么保证RabbitMQ消息的可靠传输的呢?

小汪:消息不可靠的原因主要是消息丢失。消息丢失分为:生产者丢失消息、消息模型丢失消息、消费者丢失消息。

1、从生产者丢数据这个角度来看,RabbitMQ提供了事务和confirm模式来确保消息不会丢失。

对于事务机制:发消息前,开启事务,然后发送消息,如果发送过程中出现异常,事务就回滚;若发送成功则提交事务。

对于confirm机制:要求生产者在发送完消息之后进行“发送确认”,当确认成功时即代表消息已经成功发送出去了;若发送失败,生产者进行重试操作。

2、从消息模型丢失消息的角度来看,设置交换机、队列、消息为持久化模式,即使RabbitMQ服务器突然宕机,也能保证服务器重启之后,交换机、队列仍旧存在而且消息不会丢失。

3、从消费者丢失消息的角度来看,消费者丢数据一般是设置了确认消费模式为“不需要确认消费”的机制,也就是NONE消费模式,该模型下,消费者监听到该消息后,无需发送反馈信息给RabbitMQ服务器,易造成消息丢失;

AUTO消费模式,也就是“自动确认”机制,消费者监听到该消息时,RabbitMQ自带的组件会自动发送Ack反馈信息给RabbitMQ服务器,之后该消息将在RabbitMQ队列中被移除。自动模式:消费者在收到消息之后,会先自动回复RabbitMQ服务器已收到消息,然后再处理该条消息。如果处理消息的逻辑存在问题,就会丢失该消息。

手动模式:与自动模式的不同之处在于,手动模式先需要消费者处理完消息的逻辑之后,然后手动编写代码通知RabbitMQ服务器去确认消费,最终这条消息才会从队列中移除,从而保证消费者不会丢失消息。

【面试官视角:这小伙子对于消息丢失的解决方案,条理挺清晰的。】

2.5、重复消费问题,如何解决?

大榜:嗯嗯,保证消息的可靠性这方面理解得不错。RabbitMQ是否存在重复消费的情况,若存在重复消费,说说为什么会产生重复消费?如何保证RabbitMQ的消息不会被重复消费呢?

小汪:RabbitMQ存在重复消费,主要原因是网络不可达。具体来说,考虑这样一种情况,消费者在消费完一条消息后向RabbitMQ服务器发送Ack命令,此时由于网络断开或者其他原因造成RabbitMQ并没有收到这个Ack命令,那么RabbitMQ不会将此条消息标记删除。在重新建立连接后,消费者还是会消费这一条消息,这就造成了重复消费。

重复消费的解决思路:在写入消息队列的数据做唯一标识,消费消息时,根据唯一标识判断该消息是否被消费过。我们也可以从业务层面去解决,就是该条消息多次传输,我们去保证多次消费该条消息不会给业务造成影响,也就是保证消息的幂等性。

2.6、如何保证RabbitMQ消息的顺序性?

大榜:如何保证RabbitMQ消息的顺序性消费呢?比如一条数据,正常顺序是增加、修改、删除,从RabbitMQ取出来后乱序,变成了 删除、增加、修改,这就导致数据的同步出现了问题。

小汪:先看看顺序错乱的场景:RabbitMQ中,一个生产者、多个消费者。比如,生产者向RabbitMQ的队列中发送了三条数据,顺序依次是data1、data2、data3,有三个消费者分别从MQ中消费这3条数据中的一条,可能的结果是消费者2先执行完操作,也就是data2先消费,这就明显错乱了。

有两种方法:第一种是保证生产者—队列—消费者是一对一对一的关系,也就是使用单线程来保证消息被消费的顺序性,因为单消费者对应于单个线程,对依次从RabbitMQ队列中取数据。

第二种方法:对消息进行编号,消费者处理消息时根据编号来处理消息。

大榜:小汪,你这回答的相当不错啊。果然,识别三日,当刮目相看。对RabbitMQ的核心概念、保证消息的可靠传输、消息不会被重复消费、保证消息顺序消费,都理解得不错,以后一般的RabbitMQ面试恐怕难不倒你了。

小汪:榜哥过奖了!我这一个多月的学习状态是:一边学习RabbitMQ,一边试着输出学习成果,这种方式对知识的消化吸收还挺快。我后面还准备研究RabbitMQ的运维知识,如何通过RabbitMQ集群,保证RabbitMQ的高可用。

大榜:好啊,我这几天也在看,到时候讨论下RabbitMQ运维,交流心得体会。

小汪:能跟着榜哥一起学习,美滋滋啊!

​ 完~