在面试应聘者的时候,如果面试官发现应聘者的简历上说项目中使用了MQ技术(如Kafka、RabbitMQ、RocketMQ),他们基本上都会问一个问题:在使用MQ时,如何确保消息100%不丢失?这个问题在实际工作中很常见。既可以考察应聘者对MQ中间件技术的掌握程度,又可以区分应聘者的能力水平。接下来,我们将从这道题出发,探讨一下你应该掌握的基础知识、答题思路,以及延伸面试的考点。案例背景以京东系统为例。用户在购买商品时,通常会选择使用京豆抵扣一部分金额。在这个过程中,交易服务和京豆服务通过MQ消息队列进行通信。下单时,交易服务向MQ消息队列发送“扣除账户X100京豆”消息,京豆服务在消费端消费该指令,实现真正的扣款操作。那么在这个过程中会遇到哪些问题呢?案例分析应该知道,在互联网面试中,引入MQ消息中间件最直接的目的是做系统解耦流控,而追本溯源是为了解决互联网系统的高可用和高性能问题。系统解耦:MQ消息队列可以隔离系统上下游环境变化带来的不稳定因素。比如无论京豆服务的系统需求如何变化,交易服务不需要做任何改变。即使京豆服务出现故障,主交易进程也可以降级京豆服务,实现交易服务与京豆服务的解耦,实现系统的高可用。流量控制:在出现尖峰等流量突然增加的情况下,MQ还可以实现流量“削峰填谷”的作用,可以根据下游的处理能力自动调整流量。但是,MQ的引入虽然实现了系统解耦流控,但同时也会带来其他问题。引入MQ消息中间件实现系统解耦,会影响系统间数据传输的一致性。在分布式系统中,如果两个节点之间存在数据同步,就会带来数据一致性的问题。同理,本讲要解决的是:消息生产者和消息消费者之间的消息数据一致性问题(即如何保证消息不丢失)。引入MQ消息中间件解决流控,会使得消费端的处理能力不足,导致消息积压。这也是你要解决的问题。所以你会发现问题往往环环相扣,面试官会借此机会考察你解题思路的连贯性,以及对知识体系的掌握程度。如何回答“使用MQ消息队列时如何保证消息不丢失”这个问题?首先你要分析几个测试点,比如:你怎么知道一条消息丢失了?哪个链接可能会丢失消息?如何保证消息不丢失?应聘者回??答时,先让面试官知道你的分析思路,然后提供解决方案:网络中的数据传输不可靠。要想解决如何不丢失消息的问题,首先要知道哪些链接可能会丢失消息。还有我们怎么知道消息丢了,最后是解决方案(而不是一上来就说自己的解决方案)。就像“建筑设计”和“建筑”体现了建筑师的思维过程,而“设计”是最终的解决方案,两者缺一不可。案例解答先来看消息丢失的环节。一条消息从生产到消费的过程可以分为三个阶段,即消息生产阶段、消息存储阶段和消息消费阶段。消息生产阶段:从消息生产到提交给MQ的过程中,只要能正常收到MQBroker的ack确认响应,就代表发送成功,所以只要返回值和异常处理好,这个阶段就不会出现Messagesarelost。消息存储阶段:该阶段通常直接交给MQ消息中间件进行保障,但需要了解其原理。比如Broker会做一份副本,保证一条消息在返回ack之前至少同步了两个节点。消息消费阶段:消费者从Broker拉取消息。只要consumer收到消息后不是立即向Broker发送消费确认,而是等到业务逻辑执行完毕再发送消费确认,也可以保证消息的完整性。丢失的。该方案看似万无一失,可以保证每个阶段都不会丢失消息,但在分布式系统中,故障是不可避免的。作为消息的生产者,你无法保证MQ是否丢失了你的消息,消费者消费了你的消息是否丢失了,所以,本着DesignforFailure的设计原则,你还是需要一个机制来检查消息是否丢失。接下来也可以给面试官解释一下消息检测是如何进行的?整体的解决思路是:在消息生产者端,为每条发送的消息分配一个全局唯一的ID,或者附加一个不断递增的版本号,然后在消费者端进行相应的版本校验。如何实施?您可以利用拦截器机制。生产端在发送消息之前,通过拦截器将消息版本号注入到消息中(版本号可以使用不断递增的ID生成,也可以使用分布式的全局唯一ID生成)。然后,消费者收到消息后,通过拦截器检测版本号的连续性或消费状态。这样做的好处是消息检测代码不会侵入业务代码,丢失的消息可以通过单独的任务定位。,以便进一步调查。这里需要注意:如果同时有多个消息生产者和消息消费者,很难通过增加版本号来实现,因为无法保证版本号的唯一性。此时只能使用全局唯一ID方案。对于消息检测,具体实现原理与版本号递增的方式一致。现在,你已经知道哪些环节(消息存储阶段,消息消费阶段)可能会出错,并且对如何检测消息丢失有了一个计划,然后给出防止消息丢失的设计方案。在回答完“如何保证消息不会丢失?”之后,面试官通常会问“如何解决消息重复消费的问题?”会进行重试,重试过程中可能会产生重复的消息,那么如何解决这个问题呢?其实这个问题可以换一种说法,就是如何解决消费者端的幂等性问题(幂等性是一个命令,任意多次执行的影响和一次执行的影响是一样的),如只要消费端具有幂等性,那么消息重复消费的问题就迎刃而解了。我们还是看扣金豆的例子,X账户的金豆数量扣100。在这个例子中,我们可以修改业务逻辑使其幂等。最简单的实现是在数据库中创建一个消息日志表,它有两个字段:消息ID和消息执行状态。这样我们消息消费的逻辑就可以改成:在消息日志表中增加一条消息记录,然后根据消息记录异步更新用户的京豆余额。因为我们每次插入前都会检查消息是否已经存在,所以不会出现一条消息被执行多次的情况,从而实现了幂等操作。当然,基于这个思路,不仅可以使用关系型数据库,还可以使用Redis代替数据库来实现唯一性约束。这里我再说一件事。要解决“消息丢失”和“消息重复消费”的问题,前提是要实现生成全球唯一ID的技术方案。这也是面试官喜欢调查的问题,你也应该掌握。在分布式系统中,全局唯一ID生成的实现方式有数据库自增主键、UUID、Redis、Twitter-Snowflake算法。我总结了几种解决方案的特点,大家可以参考一下。在此提醒大家,无论采用哪种方式,如果要同时满足简单性、高可用和高性能,都会有trade-off,所以要站在实际业务中说明什么是您选择时考虑的平衡。我个人在业务上更倾向于选择Snowflake算法,并且在项目中也进行了一定的改造,主要是让算法中的ID生成规则更符合业务特点,优化时钟回调等问题。当然除了“消息重复消费的问题怎么解决?”,面试官还会问你“消息积压”。原因是消息积压反映了性能问题,解决消息积压问题可以说明候选人有能力处理高并发场景下的消费能力问题。当你回答这个问题的时候,你还是要把思考的过程传递给面试官:如果有积压,那一定是性能问题。如果要解决从生产到消费的性能问题,首先要知道哪些环节可能会出现消息积压,然后我在想怎么解决。因为积压问题只有在消息发送后才会出现,与消息生产端无关,而且由于消息队列的单节点大多可以达到每秒数万的处理能力,相比于业务逻辑,性能不好。它将出现在中间件的消息存储中。毫无疑问,问题一定出在消息消费阶段,那么从消费端如何回答呢?如果线上突然出现问题,需要临时扩容增加消费者数量,同时降级一些非核心业务。通过扩容和降级来承担流量,就是展示你处理突发问题的能力。二是排查解决异常问题,比如通过监控、日志等分析消费端的业务逻辑代码是否有问题,优化消费端的业务处理逻辑。最后,如果消费端的处理能力不足,可以通过水平扩展的方式提供消费端的并发处理能力。但是有一个测试点需要特别注意,就是在扩展consumer实例数的同时,topic分区也要同步扩展Quantity,保证consumer实例数和分区数相等。如果consumer实例的数量超过了partition的数量,由于partition是单线程消费的,这样的扩容是没有效果的。比如在Kafka中,一个topic可以配置多个partition,数据会写入多个partition。但是在消费的时候,Kafka约定一个partition只能被一个consumer消费,topic中partition的数量决定了consumer的数量,因此可以通过增加partition来提高consumer的处理能力。综上所述,我们已经讲解了MQ消息队列常见问题的解决方案。无论你是初级还是高级研发工程师,本文的内容都是你需要掌握的。你可以从这几点出发,和面试官一起讨论。友好交流。让我总结一下今天的重点。如何保证消息不丢失?你需要知道一条消息从发送到消费的每一个阶段,是否有消息丢失,如何监控消息是否丢失,最后如何解决问题。解决方案可以基于“MQ可靠消息传递”的方法。如何保证消息不被重复消费?在进行消息补偿的时候,必然会有重复的消息,那么如何实现消费者端的幂等性是本题的考点。如何处理消息积压问题?这道题的考点是如何通过MQ实现真正的高性能。答案的思路是以解决线上异常为最高优先级,然后通过监控和日志来检查和优化业务逻辑,最后扩展消费端和分发。片数。在回答问题的时候,需要特别注意让面试官了解你的思考过程。这种解决问题的能力更受面试官看重,比你直接回答一个面试问题更有价值。另外,如果你应聘的部门是基础设施部门,除了掌握本讲常见问题的主线知识外,你还必须掌握消息中间件的其他知识体系,比如:如何选择消息中间件?消息中间件中的队列模型和发布订阅模型有什么区别?为什么消息队列可以做到高吞吐?序列化、传输协议和内存管理问题...>
