说如何在面试中展示消息队列方面的技能
本人在之前的文章里,给出了比较多的关于分布式组件方面技巧的准备方法,在这篇文章里,就直接Java初级和高级开发,讲讲消息队列方面说辞的准备方法。
首先可以在介绍项目时说,我们项目里用到消息中间件,比如RocketMQ,而且在项目里,我除了使用响应的API以外,还深入了解了关于消息队列的相关问题。这样面试官就会深入问了,如下给出了常用问题的准备方式和可以采用的说辞。
问题1,你们项目为什么要用消息队列,结合一个场景说一下?
先结合业务说下情况:我们用消息队列是用RocketMQ,引入消息队列的主要目的是削峰填谷。比如我们做的是线下商城下订单的业务,这个过程会涉及到创建订单、更新库存信息、更新活动预算和扣减积分等等。
我们事先做了预估,假设每个方法的耗时是200ms,那么整条链路走完就要耗费800ms,在高并发的场景里,这个时间无法接受。
为了提升性能,所以我们会把一些对实时要求不高的请求,比如上文里的更新预算和更新活动积分,用消息中间件的异步方式去处理。但是异步操作会才导致消息未必收到等问题,所以在引入消息中间件后,会通过基于JOB的重试机制外加对账核对的平台,确保因消息队列导致的不稳定因素能经过程序或人为对账处理。
这里如果面试官还有耐心,你可以说,具体地,在项目里我们是用到了Spring Boot整合RocketMQ组件的方式,事先可以准备些API和配置方式,以待面试官细问,当然最后可以再说下使用后的好处。使用后我们的交易链路能承受更高并发量的挑战。
这里需要注意的是,如果你的项目业务不是如上文所述的订单系统,那么一定得结合项目业务,找个使用消息中间件的业务场景,结合业务说。
问题2,你们为什么使用RocketMQ,或是消息中间件是如何选型的?
这里千万要记得,一般的开发是没法参与技术选型的,而且不少人进项目组以后,各种技术已经是定下来了,也没法参与选型,所以可以按如下的思路。
1 这个是我们项目经理定的,我进到组里已经定好了。这一般也是事实。
2 但我观察了一下,我们系统高并发压力比较大,而且需要引入顺序消息和事务消息,所以使用RocketMQ。
3 我还知道其它的一些消息中间件,然后再对比说各种中间件时,可以参考如下的表格。
由于RocketQM能支持10万级的并发量,而且性能不错,支持我们用的Java语言,而且支持顺序和事务消息,相比之下其它中间件虽然好,但无法切合我们的需求,所以我们是用RocketMQ。
3 如何确保消息中间件的可靠性?
从表现形式上来看,消息中间件的不可靠性主要体现在生产者丢失消息、消息中间件(或消息队列)丢失消息和消费者丢失消息3个方面。
这里务必注意,如果求职者是在学习项目中用消息中间件,或者是在培训班里用到消息中间件,一般是只会用到API和配置,一定不会参与异常处理,也就是说,说好这方面异常处理的说辞,一方面能证明自己是在商业项目里用到消息中间件,另一方面能证明自己在这方面的资深能力。
先说生产者丢失消息的情况,这种情况的可能点在于生产者程序发送失败抛出异常了没有重试处理,或是发送成功但网络问题导致消息中间件没收到,这样消息就丢失了。
由于引入消息中间件以后,一般是对消息进行异步处理,而异步发送一般分为两个方式:异步有回调和异步无回调,无回调的方式,生产者发送完以后不管,这样就可能造成消息丢失,而通过异步发送+回调通知+本地消息表的流程,就可以针对性地设计一个解决方案,比如还是以下单的业务来举例。
下单后,先在本地数据和MQ消息表里插入一条数据,这时该数据的消息状态是发送中,如本地事务失败,那么下单失败,事务回滚。
下单成功,直接返回客户端成功,异步发送消息
MQ回调,返回消息发送结果,生产者端对应地更新数据库MQ发送状态
引入JOB轮询机制,轮询超过一定时间还未发送成功,重发消息
一般系统都会有监控平台,比如Zabbix或CAT等,在监控平台配置或者JOB轮询程序处理超过一定次数,对于这些一直发送不成功的消息,告警,人工介入。
对应的流程如下所示,此时求职者甚至可以在面试前,根据自己项目的业务准备好这个流程图,面试时边说边画,这样一定能展示自己的能力。
再分析下消息中间件丢失消息的情况。如果生产者的消息发送到MQ,但MQ的消息后还在内存中,此时系统宕机,就有可能导致消息丢失。
具体在RocketMQ方面,RocketMQ一般有同步更新到硬盘和异步更新到硬盘两种方式,默认的是异步更新到硬盘,这就有可能在消息还未更新到硬盘上就丢失了。这种情况下就可以通过设置为同步更新的方式来保证消息可靠性,这样即使MQ挂了,恢复的时候也可以从磁盘中去恢复消息。
再说说消费者丢失消息的情况,比如消费者刚收到消息,此时系统宕机,MQ认为消费者已经消费,不会重复发送消息,导致消息丢失。
我们使用的RocketMQ默认是需要消费者回复ack确认,由于消费方没有返回ack确认消息,根据重发机制就会重发,如果重试超过次数之后会进入死信队列,当死信队列超过一定的数量,同样会导致Zabbix等组件告警,这时就要手工处理了。
4 消费失败导致消息积压,你们时怎么处理的?
消费失败一般需要从如下几个角度来分析。
首先排查消费者程序,如果有问题,先修复再说。
再系统上线等场景里,等不及排查并修复消费者程序,那么会根据预案,准备个临时的consumer消费着,先消息消费,然后再转发到一个新的topic和MQ资源,这个新的topic和消息队列同样需要挂到Java处理程序。
在处理好堆积消息后,等空闲下来修复消费者程序,用修复后的消费者程序消费新到达下消息。
5 你能说说RocketMQ的细节或通讯流程吗?
这块绝对是对资深开发的要求,但Java初级开发在准备好以后,未必不能说。当大家结合下图详细说好通讯流程和细节后,应该能给面试官留下“熟悉细节”的印象。
具体来讲,RocketMQ是由NameServer注册中心集群、Producer生产者集群、Consumer消费者集群和若干Broker(RocketMQ进程)组成,其大致的通讯流程一般如下。
Broker在启动时,向所有的NameServer注册,并保持长连接,每30s(这个时间可配)发送一次心跳。
具体的效果如下图所示。
6 能否解释下RocketMQ为什么高效的原因?
总体上来看,是因为引入了顺序存储、Page Cache和异步更新到硬盘等机制。
写入commitlog(提交日志)时是顺序写的,这比随机写的性能要高很多。
写入commitlog时并不是直接写入硬盘,而是先写入操作系统的PageCache,再由OS异步地把PageCache里的内容更新到硬盘里。
这里顺带说下,这个问题大家可以准备下,面试时可以在介绍项目或其它场景顺带说下,我知道RocketMQ高性能的原因,等面试官提问了再展开说明。
总结
本文根据业务场景,结合RocketMQ的细节,讲述了在面试中展示消息中间件技能的方法,请注意,大家在准备时,一方面要结合项目说,另一方面需要讲清楚异常处理的机制,这样就能在面试中,给面试官留下熟悉消息中间件,并在项目里用过,且熟悉相关细节的印象。这对3年工作经验以下的初级开发来说,是非常有利的。