面试:据说你用过MQ,能具体说说吗?

点击上方Java学习指南关注公众号
java

天天阅读Java干货文章mysql


为何使用消息队列?消息队列的优势和缺点?kafka、activemq、rabbitmq、rocketmq都有什么优缺点?web

面试官角度分析:面试

(1)你知不知道大家系统里为何要用消息队列这个东西?redis

(2)既然用了消息队列这个东西,你知不知道用了有什么好处?spring

(3)既然你用了MQ,那么当时为何选用这一款MQ?sql

1. 为何使用消息队列?

面试官问这个问题的指望之一的回答是,大家公司有什么业务场景,这个业务场景有什么技术挑战,若是不用MQ可能会很麻烦,可是再用了以后带来了不少好处。数据库

消息队列的常见使用场景有不少可是核心的有三个:解耦、异步、削峰api

解耦

场景描述:A系统发送个数据到BCD三个系统,接口调用发送,那若是E系统也要这个数据呢?那若是C系统如今不须要了呢?如今A系统又要发送第二种数据了呢?A系统负责人崩溃中...再来点崩溃的事儿,A系统要时时刻刻考虑BCDE四个系统若是挂了怎么办?那我要不要重发?我要不要把消息存起来?头发都白了啊...性能优化

使用了MQ以后的解耦场景

面试技巧:你须要考虑下,你负责的系统中是否有相似的场景,就是一个系统或者一个模块,调用了多个系统或者模块,相互之间的调用很复杂,维护起来很麻烦。可是其实这个调用是不须要直接同步调用接口的,若是MQ给他异步化解耦也是能够的,你就须要去考虑在你的项目里是否是能够运用这个MQ去进行系统解耦 。

异步

场景描述:系统A接受一个请求,须要在本身本地写库,还须要在系统BCD三个系统写库,本身本地写库须要3ms。BCD分别须要300ms、450ms、200ms。最终总好时长:953ms,接近1s。给用户的体验感受一点也很差。

不用MQ的同步高延时请求场景

使用MQ异步化以后的接口性能优化

削峰

场景描述:天天 0 点到 11 点,系统A风平浪静,每秒并发请求数量就 100 个。结果每一一到11点到1点,每秒并发请求数量就会暴增大1万条 。可是系统最大的处理能力就只能每秒钟处理1000个请求。往期面试题汇总:001期~150期汇总

没有用MQ的时候高峰期系统被打死的场景

使用MQ来进行削峰的场景

2. 消息队列的有点和缺点?

优势:特殊场景下解耦、异步、削峰。

缺点:

系统可用性下降:系统引入的外部依赖越多,越容易挂掉,原本你就是A系统调用BCD三个系统的接口就行了,人ABCD四个系统好好的没什么问题,你偏加个MQ进来,万一MQ挂了怎么办,整套系统崩溃了,就完蛋了

系统复杂性提升:硬生生加个MQ进来,你怎么保证消息没有重复消费?怎么处理消息丢失的状况?怎么保证消息传递的顺序性?

一致性问题:系统A处理完了直接返回成功了,人家都认为你这个请求成功了;但问题是,要是BCD三个系统哪里BD系统成功了,结果C系统写库失败了,咋整?数据就不一致了,

因此消息队列是一种很是复杂的架构,引入它有不少好处,可是也得针对他带来的坏处作各类额外的技术方案和架构来规避掉。作好以后你会发现系统复杂度提高了一个数量积,可是关键时刻,用,仍是要用的。

3. kafka、activemq、rabbitmq、rocketmq都有什么优缺点?

特性

ActiveMQ

RabbitMQ

RocketMQ

Kafka

单机吞吐量

万级,吞吐量比RocketMQ和Kafka要低了一个数量级

万级,吞吐量比RocketMQ和Kafka要低了一个数量级

10万级,RocketMQ也是能够支撑高吞吐的一种MQ

10万级别,这是kafka最大的优势,就是吞吐量高。

 

通常配合大数据类的系统来进行实时数据计算、日志采集等场景

topic数量对吞吐量的影响



topic能够达到几百,几千个的级别,吞吐量会有较小幅度的降低

这是RocketMQ的一大优点,在同等机器下,能够支撑大量的topic

topic从几十个到几百个的时候,吞吐量会大幅度降低

因此在同等机器下,kafka尽可能保证topic数量不要过多。若是要支撑大规模topic,须要增长更多的机器资源

时效性

ms级

微秒级,这是rabbitmq的一大特色,延迟是最低的

ms级

延迟在ms级之内

可用性

 

高,基于主从架构实现高可用性

高,基于主从架构实现高可用性

很是高,分布式架构

很是高,kafka是分布式的,一个数据多个副本,少数机器宕机,不会丢失数据,不会致使不可用

消息可靠性

有较低的几率丢失数据


通过参数优化配置,能够作到0丢失

通过参数优化配置,消息能够作到0丢失

功能支持

MQ领域的功能极其完备

基于erlang开发,因此并发能力很强,性能极其好,延时很低

MQ功能较为完善,仍是分布式的,扩展性好

功能较为简单,主要支持简单的MQ功能,在大数据领域的实时计算以及日志采集被大规模使用,是事实上的标准

优劣势总结

很是成熟,功能强大,在业内大量的公司以及项目中都有应用

 

偶尔会有较低几率丢失消息

 

并且如今社区以及国内应用都愈来愈少,官方社区如今对ActiveMQ 5.x维护愈来愈少,几个月才发布一个版本

 

并且确实主要是基于解耦和异步来用的,较少在大规模吞吐的场景中使用

 

erlang语言开发,性能极其好,延时很低;

吞吐量到万级,MQ功能比较完备

并且开源提供的管理界面很是棒,用起来很好用

社区相对比较活跃,几乎每月都发布几个版本分

在国内一些互联网公司近几年用rabbitmq也比较多一些

可是问题也是显而易见的,RabbitMQ确实吞吐量会低一些,这是由于他作的实现机制比较重。

并且erlang开发,国内有几个公司有实力作erlang源码级别的研究和定制?若是说你没这个实力的话,确实偶尔会有一些问题,你很难去看懂源码,你公司对这个东西的掌控很弱,基本职能依赖于开源社区的快速维护和修复bug。

并且rabbitmq集群动态扩展会很麻烦,不过这个我以为还好。其实主要是erlang语言自己带来的问题。很难读源码,很难定制和掌控。

接口简单易用,并且毕竟在阿里大规模应用过,有阿里品牌保障

日处理消息上百亿之多,能够作到大规模吞吐,性能也很是好,分布式扩展也很方便,社区维护还能够,可靠性和可用性都是ok的,还能够支撑大规模的topic数量,支持复杂MQ业务场景

并且一个很大的优点在于,阿里出品都是java系的,咱们能够本身阅读源码,定制本身公司的MQ,能够掌控

社区活跃度相对较为通常,不过也还能够,文档相对来讲简单一些,而后接口这块不是按照标准JMS规范走的有些系统要迁移须要修改大量代码

还有就是阿里出台的技术,你得作好这个技术万一被抛弃,社区黄掉的风险,那若是大家公司有技术实力我以为用RocketMQ挺好的

kafka的特色其实很明显,就是仅仅提供较少的核心功能,可是提供超高的吞吐量,ms级的延迟,极高的可用性以及可靠性,并且分布式能够任意扩展

 

同时kafka最好是支撑较少的topic数量便可,保证其超高吞吐量

 

并且kafka惟一的一点劣势是有可能消息重复消费,那么对数据准确性会形成极其轻微的影响,在大数据领域中以及日志采集中,这点轻微影响能够忽略

 

这个特性自然适合大数据实时计算以及日志收集

1. 引入消息队列以后如何保证其高可用性?

(1)RabbitMQ的高可用性

RabbitMQ是比较有表明性的,由于是基于主从作高可用性的,咱们就以他为例子讲解第一种MQ的高可用性怎么实现。

rabbitmq有三种模式:单机模式,普通集群模式,镜像集群模式

(1.1) 单机模式

就是demo级别的,通常就是你本地启动了玩玩儿的,没人生产用单机模式

(1.2)普通集群模式

意思就是在多台机器上启动多个rabbitmq实例,每一个机器启动一个。可是你建立的queue,只会放在一个rabbtimq实例上,可是每一个实例都同步queue的元数据。完了你消费的时候,实际上若是链接到了另一个实例,那么那个实例会从queue所在实例上拉取数据过来。

这种方式确实很麻烦,也不怎么好,没作到所谓的分布式,就是个普通集群。由于这致使你要么消费者每次随机链接一个实例而后拉取数据,要么固定链接那个queue所在实例消费数据,前者有数据拉取的开销,后者致使单实例性能瓶颈。

往期面试题汇总:001期~150期汇总

并且若是那个放queue的实例宕机了,会致使接下来其余实例就没法从那个实例拉取,若是你开启了消息持久化,让rabbitmq落地存储消息的话,消息不必定会丢,得等这个实例恢复了,而后才能够继续从这个queue拉取数据。

因此这个事儿就比较尴尬了,这就没有什么所谓的高可用性可言了,这方案主要是提升吞吐量的,就是说让集群中多个节点来服务某个queue的读写操做。

(1.3)镜像集群模式

这种模式,才是所谓的rabbitmq的高可用模式,跟普通集群模式不同的是,你建立的queue,不管元数据仍是queue里的消息都会存在于多个实例上,而后每次你写消息到queue的时候,都会自动把消息到多个实例的queue里进行消息同步。

这样的话,好处在于,你任何一个机器宕机了,没事儿,别的机器均可以用。坏处在于,第一,这个性能开销也太大了吧,消息同步全部机器,致使网络带宽压力和消耗很重!第二,这么玩儿,就没有扩展性可言了,若是某个queue负载很重,你加机器,新增的机器也包含了这个queue的全部数据,并无办法线性扩展你的queue

那么怎么开启这个镜像集群模式呢?我这里简单说一下,避免面试人家问你你不知道,其实很简单rabbitmq有很好的管理控制台,就是在后台新增一个策略,这个策略是镜像集群模式的策略,指定的时候能够要求数据同步到全部节点的,也能够要求就同步到指定数量的节点,而后你再次建立queue的时候,应用这个策略,就会自动将数据同步到其余的节点上去了。

(2)kafka的高可用性

kafka一个最基本的架构认识:多个broker组成,每一个broker是一个节点;你建立一个topic,这个topic能够划分为多个partition,每一个partition能够存在于不一样的broker上,每一个partition就放一部分数据。

这就是自然的分布式消息队列,就是说一个topic的数据,是分散放在多个机器上的,每一个机器就放一部分数据。

实际上rabbitmq之类的,并非分布式消息队列,他就是传统的消息队列,只不过提供了一些集群、HA的机制而已,由于不管怎么玩儿,rabbitmq一个queue的数据都是放在一个节点里的,镜像集群下,也是每一个节点都放这个queue的完整数据。

kafka 0.8之前,是没有HA机制的,就是任何一个broker宕机了,那个broker上的partition就废了,无法写也无法读,没有什么高可用性可言。

kafka 0.8之后,提供了HA机制,就是replica副本机制。每一个partition的数据都会同步到吉他机器上,造成本身的多个replica副本。而后全部replica会选举一个leader出来,那么生产和消费都跟这个leader打交道,而后其余replica就是follower。写的时候,leader会负责把数据同步到全部follower上去,读的时候就直接读leader上数据便可。只能读写leader?很简单,要是你能够随意读写每一个follower,那么就要care数据一致性的问题,系统复杂度过高,很容易出问题。kafka会均匀的将一个partition的全部replica分布在不一样的机器上,这样才能够提升容错性。

这么搞,就有所谓的高可用性了,由于若是某个broker宕机了,没事儿,那个broker上面的partition在其余机器上都有副本的,若是这上面有某个partition的leader,那么此时会从新选举一个新的leader出来,你们继续读写那个新的leader便可。这就有所谓的高可用性了。

写数据的时候,生产者就写leader,而后leader将数据落地写本地磁盘,接着其余follower本身主动从leader来pull数据。一旦全部follower同步好数据了,就会发送ack给leader,leader收到全部follower的ack以后,就会返回写成功的消息给生产者。(固然,这只是其中一种模式,还能够适当调整这个行为)

消费的时候,只会从leader去读,可是只有一个消息已经被全部follower都同步成功返回ack的时候,这个消息才会被消费者读到。

实际上这块机制,讲深了,是能够很是之深刻的,可是我仍是回到咱们这个课程的主题和定位,聚焦面试,至少你听到这里大体明白了kafka是如何保证高可用机制的了,对吧?不至于一无所知,现场还能给面试官画画图。要赶上面试官确实是kafka高手,深挖了问,那你只能说很差意思,太深刻的你没研究过。

可是你们必定要明白,这个事情是要权衡的,你如今是要快速突击常见面试题体系,而不是要深刻学习kafka,要深刻学习kafka,你是没那么多时间的。你只能确保,你以前也许压根儿不知道这块,可是如今你知道了,面试被问到,你大概能够说一说。而后不少其余的候选人,也许还不如你,没看过这个,被问到了压根儿答不出来,相比之下,你还能说点出来,大概就是这个意思了。

2. 如何保证消息不被重复消费(如何保证消息消费时的幂等性)?

其实这个很常见的一个问题,这俩问题基本能够连起来问。既然是消费消息,那确定要考虑考虑会不会重复消费?能不能避免重复消费?或者重复消费了也别形成系统异常能够吗?这个是MQ领域的基本问题,其实本质上仍是问你使用消息队列如何保证幂等性,这个是你架构里要考虑的一个问题。

首先就是好比rabbitmq、rocketmq、kafka,都有可能会出现消费重复消费的问题,正常。由于这问题一般不是mq本身保证的,是给你保证的。而后咱们挑一个kafka来举个例子,说说怎么重复消费吧。

kafka实际上有个offset的概念,就是每一个消息写进去,都有一个offset,表明他的序号,而后consumer消费了数据以后,每隔一段时间,会把本身消费过的消息的offset提交一下,表明我已经消费过了,下次我要是重启啥的,你就让我继续从上次消费到的offset来继续消费吧。

可是凡事总有意外,好比咱们以前生产常常遇到的,就是你有时候重启系统,看你怎么重启了,若是碰到点着急的,直接kill进程了,再重启。这会致使consumer有些消息处理了,可是没来得及提交offset,尴尬了。重启以后,少数消息会再次消费一次。

其实重复消费不可怕,可怕的是你没考虑到重复消费以后,怎么保证幂等性。

给你举个例子吧。假设你有个系统,消费一条往数据库里插入一条,要是你一个消息重复两次,你不就插入了两条,这数据不就错了?可是你要是消费到第二次的时候,本身判断一下已经消费过了,直接扔了,不就保留了一条数据?

一条数据重复出现两次,数据库里就只有一条数据,这就保证了系统的幂等性

幂等性,我通俗点说,就一个数据,或者一个请求,给你重复来屡次,你得确保对应的数据是不会改变的,不能出错。

那因此第二个问题来了,怎么保证消息队列消费的幂等性?

其实仍是得结合业务来思考,我这里给几个思路:

(1)好比你拿个数据要写库,你先根据主键查一下,若是这数据都有了,你就别插入了,update一下好吧

(2)好比你是写redis,那没问题了,反正每次都是set,自然幂等性

(3)好比你不是上面两个场景,那作的稍微复杂一点,你须要让生产者发送每条数据的时候,里面加一个全局惟一的id,相似订单id之类的东西,而后你这里消费到了以后,先根据这个id去好比redis里查一下,以前消费过吗?若是没有消费过,你就处理,而后这个id写redis。若是消费过了,那你就别处理了,保证别重复处理相同的消息便可。

还有好比基于数据库的惟一键来保证重复数据不会重复插入多条,咱们以前线上系统就有这个问题,就是拿到数据的时候,每次重启可能会有重复,由于kafka消费者还没来得及提交offset,重复数据拿到了之后咱们插入的时候,由于有惟一键约束了,因此重复数据只会插入报错,不会致使数据库中出现脏数据

如何保证MQ的消费是幂等性的,须要结合具体的业务来看

如何保证消息的幂等性

3. 如何保证消息的可靠传输(如何处理消息丢失的问题)?

这个是确定的,用mq有个基本原则,就是数据不能多一条,也不能少一条,不能多,就是刚才说的重复消费和幂等性问题。不能少,就是说这数据别搞丢了。那这个问题你必须得考虑一下。

若是说你这个是用mq来传递很是核心的消息,好比说计费,扣费的一些消息,由于我之前设计和研发过一个公司很是核心的广告平台,计费系统,计费系统是很重的一个业务,操做是很耗时的。因此说广告系统总体的架构里面,其实是将计费作成异步化的,而后中间就是加了一个MQ。

咱们当时为了确保说这个MQ传递过程当中绝对不会把计费消息给弄丢,花了不少的精力。广告主投放了一个广告,明明说好了,用户点击一次扣费1块钱。结果要是用户动不动点击了一次,扣费的时候搞的消息丢了,咱们公司就会不断的少几块钱,几块钱,聚沙成塔,这个就对公司是一个很大的损失。

面试题剖析

这个丢数据,mq通常分为两种,要么是mq本身弄丢了,要么是咱们消费的时候弄丢了。我们从rabbitmq和kafka分别来分析一下

rabbitmq这种mq,通常来讲都是承载公司的核心业务的,数据是绝对不能弄丢的

RabbitMQ可能存在的数据丢失问题

(1)rabbitmq

1)生产者弄丢了数据

生产者将数据发送到rabbitmq的时候,可能数据就在半路给搞丢了,由于网络啥的问题,都有可能。

此时能够选择用rabbitmq提供的事务功能,就是生产者发送数据以前开启rabbitmq事务(channel.txSelect),而后发送消息,若是消息没有成功被rabbitmq接收到,那么生产者会收到异常报错,此时就能够回滚事务(channel.txRollback),而后重试发送消息;若是收到了消息,那么能够提交事务(channel.txCommit)。可是问题是,rabbitmq事务机制一搞,基本上吞吐量会下来,由于太耗性能。

因此通常来讲,若是你要确保说写rabbitmq的消息别丢,能够开启confirm模式,在生产者那里设置开启confirm模式以后,你每次写的消息都会分配一个惟一的id,而后若是写入了rabbitmq中,rabbitmq会给你回传一个ack消息,告诉你说这个消息ok了。若是rabbitmq没能处理这个消息,会回调你一个nack接口,告诉你这个消息接收失败,你能够重试。并且你能够结合这个机制本身在内存里维护每一个消息id的状态,若是超过必定时间还没接收到这个消息的回调,那么你能够重发。

事务机制和cnofirm机制最大的不一样在于,事务机制是同步的,你提交一个事务以后会阻塞在那儿,可是confirm机制是异步的,你发送个消息以后就能够发送下一个消息,而后那个消息rabbitmq接收了以后会异步回调你一个接口通知你这个消息接收到了。

因此通常在生产者这块避免数据丢失,都是用confirm机制的。

2)rabbitmq弄丢了数据

就是rabbitmq本身弄丢了数据,这个你必须开启rabbitmq的持久化,就是消息写入以后会持久化到磁盘,哪怕是rabbitmq本身挂了,恢复以后会自动读取以前存储的数据,通常数据不会丢。除非极其罕见的是,rabbitmq还没持久化,本身就挂了,可能致使少许数据会丢失的,可是这个几率较小。

设置持久化有两个步骤,第一个是建立queue的时候将其设置为持久化的,这样就能够保证rabbitmq持久化queue的元数据,可是不会持久化queue里的数据;第二个是发送消息的时候将消息的deliveryMode设置为2,就是将消息设置为持久化的,此时rabbitmq就会将消息持久化到磁盘上去。必需要同时设置这两个持久化才行,rabbitmq哪怕是挂了,再次重启,也会从磁盘上重启恢复queue,恢复这个queue里的数据。

并且持久化能够跟生产者那边的confirm机制配合起来,只有消息被持久化到磁盘以后,才会通知生产者ack了,因此哪怕是在持久化到磁盘以前,rabbitmq挂了,数据丢了,生产者收不到ack,你也是能够本身重发的。

哪怕是你给rabbitmq开启了持久化机制,也有一种可能,就是这个消息写到了rabbitmq中,可是还没来得及持久化到磁盘上,结果不巧,此时rabbitmq挂了,就会致使内存里的一点点数据会丢失。

3)消费端弄丢了数据

rabbitmq若是丢失了数据,主要是由于你消费的时候,刚消费到,还没处理,结果进程挂了,好比重启了,那么就尴尬了,rabbitmq认为你都消费了,这数据就丢了。

这个时候得用rabbitmq提供的ack机制,简单来讲,就是你关闭rabbitmq自动ack,能够经过一个api来调用就行,而后每次你本身代码里确保处理完的时候,再程序里ack一把。这样的话,若是你还没处理完,不就没有ack?那rabbitmq就认为你还没处理完,这个时候rabbitmq会把这个消费分配给别的consumer去处理,消息是不会丢的。

往期面试题汇总:001期~150期汇总

(2)kafka

1)消费端弄丢了数据

惟一可能致使消费者弄丢数据的状况,就是说,你那个消费到了这个消息,而后消费者那边自动提交了offset,让kafka觉得你已经消费好了这个消息,其实你刚准备处理这个消息,你还没处理,你本身就挂了,此时这条消息就丢咯。

这不是同样么,你们都知道kafka会自动提交offset,那么只要关闭自动提交offset,在处理完以后本身手动提交offset,就能够保证数据不会丢。可是此时确实仍是会重复消费,好比你刚处理完,还没提交offset,结果本身挂了,此时确定会重复消费一次,本身保证幂等性就行了。

生产环境碰到的一个问题,就是说咱们的kafka消费者消费到了数据以后是写到一个内存的queue里先缓冲一下,结果有的时候,你刚把消息写入内存queue,而后消费者会自动提交offset。

而后此时咱们重启了系统,就会致使内存queue里还没来得及处理的数据就丢失了

2)kafka弄丢了数据

这块比较常见的一个场景,就是kafka某个broker宕机,而后从新选举partiton的leader时。你们想一想,要是此时其余的follower恰好还有些数据没有同步,结果此时leader挂了,而后选举某个follower成leader以后,他不就少了一些数据?这就丢了一些数据啊。

生产环境也遇到过,咱们也是,以前kafka的leader机器宕机了,将follower切换为leader以后,就会发现说这个数据就丢了

因此此时通常是要求起码设置以下4个参数:

  • 给这个topic设置replication.factor参数:这个值必须大于1,要求每一个partition必须有至少2个副本

  • 在kafka服务端设置min.insync.replicas参数:这个值必须大于1,这个是要求一个leader至少感知到有至少一个follower还跟本身保持联系,没掉队,这样才能确保leader挂了还有一个follower吧

  • 在producer端设置acks=all:这个是要求每条数据,必须是写入全部replica以后,才能认为是写成功了

  • 在producer端设置retries=MAX(很大很大很大的一个值,无限次重试的意思):这个是要求一旦写入失败,就无限重试,卡在这里了

咱们生产环境就是按照上述要求配置的,这样配置以后,至少在kafka broker端就能够保证在leader所在broker发生故障,进行leader切换时,数据不会丢失

3)生产者会不会弄丢数据

若是按照上述的思路设置了ack=all,必定不会丢,要求是,你的leader接收到消息,全部的follower都同步到了消息以后,才认为本次写成功了。若是没知足这个条件,生产者会自动不断的重试,重试无限次。

1. 如何保证消息的顺序性?

其实这个也是用MQ的时候必问的话题,第一看看你了解不了解顺序这个事儿?第二看看你有没有办法保证消息是有顺序的?这个生产系统中常见的问题。

我举个例子,咱们之前作过一个mysql binlog同步的系统,压力仍是很是大的,日同步数据要达到上亿。mysql -> mysql,常见的一点在于说大数据team,就须要同步一个mysql库过来,对公司的业务系统的数据作各类复杂的操做。

你在mysql里增删改一条数据,对应出来了增删改3条binlog,接着这三条binlog发送到MQ里面,到消费出来依次执行,起码得保证人家是按照顺序来的吧?否则原本是:增长、修改、删除;你楞是换了顺序给执行成删除、修改、增长,不全错了么。

原本这个数据同步过来,应该最后这个数据被删除了;结果你搞错了这个顺序,最后这个数据保留下来了,数据同步就出错了。

先看看顺序会错乱的俩场景

(1)rabbitmq:一个queue,多个consumer,这不明显乱了

(2)kafka:一个topic,一个partition,一个consumer,内部多线程,这不也明显乱了

那如何保证消息的顺序性呢?简单简单

(1)rabbitmq:拆分多个queue,每一个queue一个consumer,就是多一些queue而已,确实是麻烦点;

或者就一个queue可是对应一个consumer,而后这个consumer内部用内存队列作排队,而后分发给底层不一样的worker来处理

(2)kafka:一个topic,一个partition,一个consumer,内部单线程消费,写N个内存queue,而后N个线程分别消费一个内存queue便可

如何解决消息队列的延时以及过时失效问题?消息队列满了之后该怎么处理?有几百万消息持续积压几小时,说说怎么解决?

你看这问法,其实本质针对的场景,都是说,可能你的消费端出了问题,不消费了,或者消费的极其极其慢。接着就坑爹了,可能你的消息队列集群的磁盘都快写满了,都没人消费,这个时候怎么办?或者是整个这就积压了几个小时,你这个时候怎么办?或者是你积压的时间太长了,致使好比rabbitmq设置了消息过时时间后就没了怎么办?

因此就这事儿,其实线上挺常见的,通常不出,一出就是大case,通常常见于,举个例子,消费端每次消费以后要写mysql,结果mysql挂了,消费端hang那儿了,不动了。或者是消费端出了个什么叉子,致使消费速度极其慢。

关于这个事儿,咱们一个一个来梳理吧,先假设一个场景,咱们如今消费端出故障了,而后大量消息在mq里积压,如今事故了,慌了


广告插入:欢迎关注纯干货技术公众号:Java学习指南,天天阅读技术文章。

(1)大量消息在mq里积压了几个小时了还没解决

几千万条数据在MQ里积压了七八个小时,从下午4点多,积压到了晚上很晚,10点多,11点多

这个是咱们真实遇到过的一个场景,确实是线上故障了,这个时候要否则就是修复consumer的问题,让他恢复消费速度,而后傻傻的等待几个小时消费完毕。这个确定不能在面试的时候说吧。

一个消费者一秒是1000条,一秒3个消费者是3000条,一分钟是18万条,1000多万条

因此若是你积压了几百万到上千万的数据,即便消费者恢复了,也须要大概1小时的时间才能恢复过来

通常这个时候,只能操做临时紧急扩容了,具体操做步骤和思路以下:

1)先修复consumer的问题,确保其恢复消费速度,而后将现有cnosumer都停掉

2)新建一个topic,partition是原来的10倍,临时创建好原先10倍或者20倍的queue数量

3)而后写一个临时的分发数据的consumer程序,这个程序部署上去消费积压的数据,消费以后不作耗时的处理,直接均匀轮询写入临时创建好的10倍数量的queue

4)接着临时征用10倍的机器来部署consumer,每一批consumer消费一个临时queue的数据

5)这种作法至关因而临时将queue资源和consumer资源扩大10倍,以正常的10倍速度来消费数据

6)等快速消费完积压数据以后,得恢复原先部署架构,从新用原先的consumer机器来消费消息

(2)这里咱们假设再来第二个坑

假设你用的是rabbitmq,rabbitmq是能够设置过时时间的,就是TTL,若是消息在queue中积压超过必定的时间就会被rabbitmq给清理掉,这个数据就没了。那这就是第二个坑了。这就不是说数据会大量积压在mq里,而是大量的数据会直接搞丢。

这个状况下,就不是说要增长consumer消费积压的消息,由于实际上没啥积压,而是丢了大量的消息。咱们能够采起一个方案,就是批量重导,这个咱们以前线上也有相似的场景干过。就是大量积压的时候,咱们当时就直接丢弃数据了,而后等过了高峰期之后,好比你们一块儿喝咖啡熬夜到晚上12点之后,用户都睡觉了。

这个时候咱们就开始写程序,将丢失的那批数据,写个临时程序,一点一点的查出来,而后从新灌入mq里面去,把白天丢的数据给他补回来。也只能是这样了。

假设1万个订单积压在mq里面,没有处理,其中1000个订单都丢了,你只能手动写程序把那1000个订单给查出来,手动发到mq里去再补一次

往期面试题汇总:001期~150期汇总

(3)而后咱们再来假设第三个坑

若是走的方式是消息积压在mq里,那么若是你很长时间都没处理掉,此时致使mq都快写满了,咋办?这个还有别的办法吗?没有,谁让你第一个方案执行的太慢了,你临时写程序,接入数据来消费,消费一个丢弃一个,都不要了,快速消费掉全部的消息。而后走第二个方案,到了晚上再补数据吧。

1. 若是让你写一个消息队列,该如何进行架构设计啊?说一下你的思路

其实聊到这个问题,通常面试官要考察两块:

(1)你有没有对某一个消息队列作过较为深刻的原理的了解,或者从总体了解把握住一个mq的架构原理

(2)看看你的设计能力,给你一个常见的系统,就是消息队列系统,看看你能不能从全局把握一下总体架构设计,给出一些关键点出来

其实回答这类问题,说白了,起码不求你看过那技术的源码,起码你大概知道那个技术的基本原理,核心组成部分,基本架构构成,而后参照一些开源的技术把一个系统设计出来的思路说一下就好

好比说这个消息队列系统,咱们来从如下几个角度来考虑一下

说实话,我通常面相似问题的时候,大部分人基本都会蒙,由于平时历来没有思考过相似的问题,大多数人就是平时埋头用,历来不去思考背后的一些东西。相似的问题,我常常问的还有,若是让你来设计一个spring框架你会怎么作?若是让你来设计一个dubbo框架你会怎么作?若是让你来设计一个mybatis框架你会怎么作?

其实回答这类问题,说白了,起码不求你看过那技术的源码,起码你大概知道那个技术的基本原理,核心组成部分,基本架构构成,而后参照一些开源的技术把一个系统设计出来的思路说一下就好

好比说这个消息队列系统,咱们来从如下几个角度来考虑一下

(1)首先这个mq得支持可伸缩性吧,就是须要的时候快速扩容,就能够增长吞吐量和容量,那怎么搞?设计个分布式的系统呗,参照一下kafka的设计理念,broker -> topic -> partition,每一个partition放一个机器,就存一部分数据。若是如今资源不够了,简单啊,给topic增长partition,而后作数据迁移,增长机器,不就能够存放更多数据,提供更高的吞吐量了?

(2)其次你得考虑一下这个mq的数据要不要落地磁盘吧?那确定要了,落磁盘,才能保证别进程挂了数据就丢了。那落磁盘的时候怎么落啊?顺序写,这样就没有磁盘随机读写的寻址开销,磁盘顺序读写的性能是很高的,这就是kafka的思路。

2.其次你考虑一下你的mq的可用性啊?

这个事儿,具体参考咱们以前可用性那个环节讲解的kafka的高可用保障机制。多副本 -> leader & follower -> broker挂了从新选举leader便可对外服务。

(4)能不能支持数据0丢失啊?能够的,参考咱们以前说的那个kafka数据零丢失方案

其实一个mq确定是很复杂的,面试官问你这个问题,实际上是个开放题,他就是看看你有没有从架构角度总体构思和设计的思惟以及能力。确实这个问题能够刷掉一大批人,由于大部分人平时不思考这些东西。

< END >

来源:blog.csdn.net/qq_29676623/article/details/85108070


- 往期精彩 -


这多是你看过最好的微服务架构详解文章


编码之路,与君共勉。


硬核干货:一个核心系统 3 万多行代码的重构之旅



全干货技术公众号Java学习指南

👇👇

长按上方二维码关注公众号


本文分享自微信公众号 - Java学习指南(gh_85b94beaede2)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索