当前位置: 首页 > 科技观察

MQ那点狗屎!消息丢失、重复消费、消费顺序、累积、事务、高可用....

时间:2023-03-15 08:42:34 科技观察

本文转载自微信公众号《微科技》,作者微科技。转载本文请联系微科公众号。大家好,我是汤姆哥~国庆假期马上就要开始了,祝大家假期愉快,吃喝玩乐~为了让大家更容易发现问题,了解全貌,并且整理了一个目录,我们可以快速全局的了解关于消息队列,面试官一般会问什么问题。本文内容:消息队列的应用场景?答:1.异步处理2.流量削峰填谷3.应用解耦4.消息通信异步处理。将非核心进程拆分在一个请求环节,异步处理,减少主流程环节的处理逻辑,缩短RT,提高吞吐量。如:注册新用户发送短信通知。削峰填谷。为避免流量激增造成下游系统宕机,会在前端增加一个消息队列来平滑流量冲击。例如:秒杀活动。这也是生活中电源适配器的原理。应用解耦。两个应用通过消息系统间接建立联系,避免了一个系统宕机后对另一个系统的影响,提高了系统的可用性。如:订单异步扣库存消息通信。内置高效的通信机制,可用于消息通信。如:点对点消息队列、聊天室。常用的消息框架有哪些?答:ActiveMQ、RabbitMQ、ZeroMQ、Kafka、MetaQ、RocketMQ、Pulsar等MQ技术选型?答:对比Kafka、RocketMQ、Pulsar三个框架,耗时、吞吐量、可靠性、事务、副本同步策略、多租户、动态扩展、故障恢复等评价指标。什么是消息模型?答:1、点对点模式2、发布/订阅模式如何保证MQ消息不丢失?答:了解了消息中间件的运行方式后,主要从三个方面考虑这个问题:1.生产端,不丢消息2.MQ服务器,存储本身不丢消息3.消费者,不丢消息不会丢失消息。如何解决消息的重复消费?答:为了保证消息发送成功,生产者可能会反复推送(直到收到成功的ACK),会产生重复的消息。但是成熟的MQServer框架一般都会想办法避免存储重复的消息(例如:空间换时间,存储处理过的message_id),并提供一个幂等的接口,用于向生产端发送消息。但是,消费端并不能从根本上解决这个问题。高并发标准要求下,需要事务处理拉取消息+业务处理+提交消费位移。另外,消费者端服务可能宕机,可能会拉取重复的消息。所以,只有业务端可以自己控制。对于消费成功的消息,每次处理前都会校验本地数据库表或Redis缓存业务标识,保证幂等性。如何保证MQ消息有序?答:有些业务是有上下文需求的,比如:电商行业的下单、支付、发货、确认收货,每个环节都会发送消息。消费者在拉取和消费消息时,也希望遵循正常的状态机流程。所以消息有顺序要求。解决思路:1.topic强制使用partition,所有的消息都放在一个queue中,实现全局有序。但是高并发特性会丢失。2、部分有序,利用路由机制将同一顺序的不同状态消息存放在一个分区中,单线程消费。比如Kafka提供了接口扩展org.apache.kafka.clients.Partitioner,方便开发者根据自己的业务场景自定义路由规则。消息堆积如何处理?答:主要是消息的消费速度跟不上生产速度,导致消息堆积。解决思路:1、可能是新上线的业务,或者是大型促销活动,流量考核不到位。这时候就需要增加消费组的机器数量来提升整体的消费能力。2、也可能是消费端的问题。正常情况下一条消息处理需要10ms,但如果优化不到位或者线上有bug,现在需要500ms,消费者端整体处理速度会下降50倍。这时候我们就需要有针对性地检查业务代码。汤姆领导的团队遇到了这个问题。当时数据库中的一条sql没有命中索引,导致单条消息的处理耗时很长,进而导致消息堆积,上线告警。但是根据我们丰富的经验,很容易定位很快就解决了。如何保证数据的一致性?答:为了解耦,引入异步消息机制。先进行本地数据库操作,处理成功后发送MQ消息,后续操作由消费者进行。例如:电商下单成功后,需要通知扣除库存。这两者必须保证事务操作,否则会出现数据不一致的问题。这时候就需要引入事务消息来解决这个问题。另外,在消费环节,也可能会出现数据不一致的情况。我们可以采用最终一致性原则,加上重试机制。交易消息是如何实现的?答:1、生产者先向MQ发送半事务消息2、MQ收到消息后返回ack确认3、生产者开始执行本地事务4、如果本地事务执行成功,则向MQ发送commit;失败,发送回滚5.如果是MQ?time没有收到producer的二次确认commit或者rollback,MQ向producer发起reversecheckback6.producer查询事务执行的最终状态7.根据查询事务状态,retry提交二次确认MQ框架是怎么做的实现高吞吐量?答:1.消息的批处理2.消息压缩,节省传输带宽和存储空间3.零拷贝4.顺序写入磁盘5.pagecachepagecache,由操作系统将缓存中的数据异步刷新到磁盘,并有效地读取内存。6、分区设计。N个分区挂载在一个逻辑主题下。每个分区可以对应不同的机器消费消息,是并发设计的。为什么kafka不支持读写分离?答:我们知道生产端消息的写入和消费者端消息的拉取都与leader副本交互。与mysql数据库不同的是,master负责写,slave负责读。本次设计主要从两个方面考虑:1.数据一致性。一主多从,leader副本的数据同步到follower副本有一定的延迟,所以每个follower副本的消息位移不同,consumer通过消费位移来控制消息拉取的进度。保持相同消耗排量的一致性。如果引入分布式锁来保证并发安全,是非常耗性能的。2.实时。leader副本的数据同步到follower副本有一定的延迟。如果网络不好,延迟会很严重,不能满足实时业务要求。综上所述,读写操作是对leader副本进行的,而follower副本主要用于数据备份。MQ框架是如何实现高可用的?答:以Kafka框架为例,其他MQ框架也有类似的原理。Kafka由多个broker组成,每个broker是一个节点。你创建一个topic,它可以分为多个partition,每个partition存储在不同的broker上,每个partition存储一部分数据,每个partition有多个replica副本。写的时候leader会负责把数据同步给所有的follower,读的时候直接读取leader上的数据即可。如果某个经纪人倒闭了,那也没关系。该代理上的分区在其他机器上有一个副本。这时候会从followers中重新选出一个新的leader,大家可以继续读写那个新的leader。这称为高可用性。关于Kafka,面试官一般喜欢调查什么问题?答:消息压缩消息解压分区策略生产者如何实现幂等和事务KafkaBroker如何存储数据?备份机制为什么要引入消费者组?

猜你喜欢