3 Star 15 Fork 6

JavaInterviewHub / JavaInterview

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
消息队列.md 18.63 KB
一键复制 编辑 原始数据 按行查看 历史
JavaInterviewHub 提交于 2021-03-04 15:35 . Update 消息队列.md

消息队列

1.消息队列有哪些应用场景?

异步处理、流量控制、服务解耦、消息广播

2.消息队列的弊端有哪些?

数据延迟;增加系统复杂度;可能产生数据不一致的问题。

3.使用消息队列,怎么确保消息不丢失?

在生产阶段,你需要捕获消息发送的错误,并重发消息。 在存储阶段,你可以通过配置刷盘和复制相关的参数,让消息写入到多个副本的磁盘上,来确保消息不会因为某个 Broker 宕机或者磁盘损坏而丢失。 在消费阶段,你需要在处理完全部消费业务逻辑之后,再发送消费确认。

4.使用消息队列,如果处理重复消息?

1)利用数据库的唯一约束实现幂等 2)为更新的数据设置前置条件(CAS) 3)记录并检查操作(在发送消息时,给每条消息指定一个全局唯一的 ID,消费时,先根据这个 ID 检查这条消息是否有被消费过,如果没有消费过,才更新数据,然后将消费状态置为已消费。)

5.Kafka的消息是有序的吗?如果保证Kafka消息的顺序性?

Kafka只能保证局部有序,即只能保证一个分区里的消息有序。而其具体实现是通过生产者为每个分区的消息维护一个发送队列,我们需要将保证顺序的消息都发送到同一个分区中。并且由于Kafka会同时发送多个消息,所以还需指定max.in.flight.requests.per.connection为1,保证前一个消息发送成功,后一个消息才开始发送。

6.消息如何保证幂等性

例如kafka的offset可能是消费者批量处理后才提交到zk,重启后再消费时就可能会收到重复消息,需要消费者在处理消息时做幂等性设计,即先判断是否消费过,把已消费的放到本地缓存或者redis中,每次消费时先做个判断即可。

7.消息队列积压怎么办

当消费者出现异常,很容易引起队列积压,如果一秒钟1000个消息,那么一个小时就是几千万的消息积压,是非常可怕的事情,但是生产线上又有可能会出现;

当消息积压来不及处理,rabbitMQ如果设置了消息过期时间,那么就有可能由于积压无法及时处理而过期,这消息就被丢失了;

解决方法如下:

  • 不建议在生产环境使用数据过期策略,一是数据是否丢失无法控制,二是一旦积压就很有可能丢失;建议数据的处理都有代码来控制;
  • 当出现消息积压时,做法就是临时扩大consumer个数,让消息快速消费,一般都是通过业务逻辑的手段来完成:如下:

【rabbitmq解决积压范例】

  1. 修复consumer代码故障,确保consumer逻辑正确可以消费;
  2. 停止consumer,开启10倍20倍的queue个数; * 创建一个临时的consumer程序,消费积压的queue,并把消息写入到扩建的10倍queue中; * 再开启10倍20倍的consumer对新的扩充后队列进行消费; * 这种做法相当于通过物理资源扩充了10倍来快速消费;
  1. 当消费完成后,需要恢复原有架构,开启原来的consumer进行正常消费;

【kafka解决范例】

  1. 修复consumer代码故障,确保consumer逻辑正确可以消费;
  2. 停止consumer,新建topic,新建10倍20倍的partition个数; * 创建对应原topic的partition个数的临时的consumer程序,消费原来的topic,并把消息写入到扩建的新topic中; * 再开启对应新partition个数的consumer对新的topic进行消费; * 这种做法相当于通过物理资源扩充了10倍来快速消费;
  1. 当消费完成后,需要恢复原有架构,开启原来的consumer进行正常消费;

8.各种MQ的比较

特性 activeMQ rabbitMQ rocketMQ kafka
单机吞吐量 万/秒 万/秒 10万/秒 10万/秒
topic对吞吐量的影响 topic达到几百/几千个级别,吞吐量会有小幅下降; 这是rocket的最大优势 所以非常适用于支撑大批量topic场景 topic可以达到几十/几百个级别,吞吐量会有大幅下降 kafka不适用大批量topic场景,除非加机器
时效性 毫秒 微秒 这是rabbit 最大优势,延迟低 毫秒 毫秒
可用性 高。主从架构 高。主从架构 非常高。分布式。 非常高。分布式。数据多副本,不会丢数据,不会不可用。
可靠性 有较低概率丢失数据 ---- 经配置优化可达到0丢失 经配置优化可达到0丢失
功能特性 功能齐全,但已不怎么维护 erlang开发,并发强,性能极好,延迟低 MQ功能较为齐全,扩展好 功能简单,主要用于大数据实时计算和日志采集,事实标准

综上,总结如下:


【activeMQ】

  • 优点:技术成熟,功能齐全,历史悠久,有大量公司在使用
  • 缺点:偶尔会有较低概率丢失数据,而且社区已经不怎么维护5.15.X版本
  • 使用场景:主要用于系统解耦和异步处理,不适用与大数据量吞吐情况。互联网公司很少适用

【rabitMQ】

  • 优点:吞吐量高,功能齐全,管理界面易用,社区活跃,性能极好,;
  • 缺点:吞吐量只是万级,erlang难以二次开发和掌控;集群动态扩展非常麻烦;
  • 使用场景:吞吐量不高而要求低延迟,并且不会频繁调整和扩展的场景。非常适合国内中小型互联网公司适用,因为管理界面非常友好,可以在界面进行配置和优化/集群监控。

【rocketMQ】

  • 优点:支持百千级大规模topic。吞吐量高(十万级,日处理上百亿)。接口易用。,分布式易扩展,阿里支持。java开发易于掌控。
  • 缺点:与阿里(社区)存在绑定。不兼容JMS规范。
  • 使用场景:高吞吐量

【kafka】

  • 优点:超高吞吐量,超高可用性和可靠性,分布式易扩展
  • 缺点:topic支持少,MQ功能简单,消息可能会重复消费影响数据精确度
  • 使用场景:超高吞吐量场景而数据精确度没那么高,天然适合大数据实时计算和日志采集场景

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

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

几千万条数据在MQ里积压了七八个小时,从下午4点多,积压到了晚上很晚,10点多,11点多 这个是我们真实遇到过的一个场景,确实是线上故障了,这个时候要不然就是修复consumer的问题,让他恢复消费速度,然后傻傻的等待几个小时消费完毕。这个肯定不能在面试的时候说吧。

一个消费者一秒是1000条,一秒3个消费者是3000条,一分钟是18万条,1000多万条,所以如果你积压了几百万到上千万的数据,即使消费者恢复了,也需要大概1小时的时间才能恢复过来。

一般这个时候,只能操作临时紧急扩容了,具体操作步骤和思路如下:

先修复consumer的问题,确保其恢复消费速度,然后将现有cnosumer都停掉。 新建一个topic,partition是原来的10倍,临时建立好原先10倍或者20倍的queue数量。 然后写一个临时的分发数据的consumer程序,这个程序部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写入临时建立好的10倍数量的queue。 接着临时征用10倍的机器来部署consumer,每一批consumer消费一个临时queue的数据。 这种做法相当于是临时将queue资源和consumer资源扩大10倍,以正常的10倍速度来消费数据。 等快速消费完积压数据之后,得恢复原先部署架构,重新用原先的consumer机器来消费消息。 (二)、消息队列过期失效问题

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

这个情况下,就不是说要增加consumer消费积压的消息,因为实际上没啥积压,而是丢了大量的消息。我们可以采取一个方案,就是批量重导,这个我们之前线上也有类似的场景干过。就是大量积压的时候,我们当时就直接丢弃数据了,然后等过了高峰期以后,比如大家一起喝咖啡熬夜到晚上12点以后,用户都睡觉了。

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

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

(三)、消息队列满了怎么搞?

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

10.为什么使用消息队列?

面试官心理分析

其实面试官主要是想看看:

  • 第一,你知不知道你们系统里为什么要用消息队列这个东西?

    不少候选人,说自己项目里用了 Redis、MQ,但是其实他并不知道自己为什么要用这个东西。其实说白了,就是为了用而用,或者是别人设计的架构,他从头到尾都没思考过。

    没有对自己的架构问过为什么的人,一定是平时没有思考的人,面试官对这类候选人印象通常很不好。因为面试官担心你进了团队之后只会木头木脑的干呆活儿,不会自己思考。

  • 第二,你既然用了消息队列这个东西,你知不知道用了有什么好处&坏处?

    你要是没考虑过这个,那你盲目弄个 MQ 进系统里,后面出了问题你是不是就自己溜了给公司留坑?你要是没考虑过引入一个技术可能存在的弊端和风险,面试官把这类候选人招进来了,基本可能就是挖坑型选手。就怕你干 1 年挖一堆坑,自己跳槽了,给公司留下无穷后患。

  • 第三,既然你用了 MQ,可能是某一种 MQ,那么你当时做没做过调研?

    你别傻乎乎的自己拍脑袋看个人喜好就瞎用了一个 MQ,比如 Kafka,甚至都从没调研过业界流行的 MQ 到底有哪几种。每一个 MQ 的优点和缺点是什么。每一个 MQ 没有绝对的好坏,但是就是看用在哪个场景可以扬长避短,利用其优势,规避其劣势。

    如果是一个不考虑技术选型的候选人招进了团队,leader 交给他一个任务,去设计个什么系统,他在里面用一些技术,可能都没考虑过选型,最后选的技术可能并不一定合适,一样是留坑。

面试题剖析

其实就是问问你消息队列都有哪些使用场景,然后你项目里具体是什么场景,说说你在这个场景里用消息队列是什么?

面试官问你这个问题,期望的一个回答是说,你们公司有个什么业务场景,这个业务场景有个什么技术挑战,如果不用 MQ 可能会很麻烦,但是你现在用了 MQ 之后带给了你很多的好处。

先说一下消息队列常见的使用场景吧,其实场景有很多,但是比较核心的有 3 个:解耦、异步、削峰。

解耦

看这么个场景。A 系统发送数据到 BCD 三个系统,通过接口调用发送。如果 E 系统也要这个数据呢?那如果 C 系统现在不需要了呢?A 系统负责人几乎崩溃......

file

在这个场景中,A 系统跟其它各种乱七八糟的系统严重耦合,A 系统产生一条比较关键的数据,很多系统都需要 A 系统将这个数据发送过来。A 系统要时时刻刻考虑 BCDE 四个系统如果挂了该咋办?要不要重发,要不要把消息存起来?头发都白了啊!

如果使用 MQ,A 系统产生一条数据,发送到 MQ 里面去,哪个系统需要数据自己去 MQ 里面消费。如果新系统需要数据,直接从 MQ 里消费即可;如果某个系统不需要这条数据了,就取消对 MQ 消息的消费即可。这样下来,A 系统压根儿不需要去考虑要给谁发送数据,不需要维护这个代码,也不需要考虑人家是否调用成功、失败超时等情况。

file

总结:通过一个 MQ,Pub/Sub 发布订阅消息这么一个模型,A 系统就跟其它系统彻底解耦了。

面试技巧:你需要去考虑一下你负责的系统中是否有类似的场景,就是一个系统或者一个模块,调用了多个系统或者模块,互相之间的调用很复杂,维护起来很麻烦。但是其实这个调用是不需要直接同步调用接口的,如果用 MQ 给它异步化解耦,也是可以的,你就需要去考虑在你的项目里,是不是可以运用这个 MQ 去进行系统的解耦。在简历中体现出来这块东西,用 MQ 作解耦。

异步

再来看一个场景,A 系统接收一个请求,需要在自己本地写库,还需要在 BCD 三个系统写库,自己本地写库要 3ms,BCD 三个系统分别写库要 300ms、450ms、200ms。最终请求总延时是 3 + 300 + 450 + 200 = 953ms,接近 1s,用户感觉搞个什么东西,慢死了慢死了。用户通过浏览器发起请求,等待个 1s,这几乎是不可接受的。

file

一般互联网类的企业,对于用户直接的操作,一般要求是每个请求都必须在 200 ms 以内完成,对用户几乎是无感知的。

如果使用 MQ,那么 A 系统连续发送 3 条消息到 MQ 队列中,假如耗时 5ms,A 系统从接受一个请求到返回响应给用户,总时长是 3 + 5 = 8ms,对于用户而言,其实感觉上就是点个按钮,8ms 以后就直接返回了,爽!网站做得真好,真快

file

削峰

每天 0:00 到 12:00,A 系统风平浪静,每秒并发请求数量就 50 个。结果每次一到 12:00 ~ 13:00 ,每秒并发请求数量突然会暴增到 5k+ 条。但是系统是直接基于 MySQL 的,大量的请求涌入 MySQL,每秒钟对 MySQL 执行约 5k 条 SQL。

一般的 MySQL,扛到每秒 2k 个请求就差不多了,如果每秒请求到 5k 的话,可能就直接把 MySQL 给打死了,导致系统崩溃,用户也就没法再使用系统了。

但是高峰期一过,到了下午的时候,就成了低峰期,可能也就 1w 的用户同时在网站上操作,每秒中的请求数量可能也就 50 个请求,对整个系统几乎没有任何的压力。

file

如果使用 MQ,每秒 5k 个请求写入 MQ,A 系统每秒钟最多处理 2k 个请求,因为 MySQL 每秒钟最多处理 2k 个。A 系统从 MQ 中慢慢拉取请求,每秒钟就拉取 2k 个请求,不要超过自己每秒能处理的最大请求数量就 ok,这样下来,哪怕是高峰期的时候,A 系统也绝对不会挂掉。而 MQ 每秒钟 5k 个请求进来,就 2k 个请求出去,结果就导致在中午高峰期(1 个小时),可能有几十万甚至几百万的请求积压在 MQ 中。

file

这个短暂的高峰期积压是 ok 的,因为高峰期过了之后,每秒钟就 50 个请求进 MQ,但是 A 系统依然会按照每秒 2k 个请求的速度在处理。所以说,只要高峰期一过,A 系统就会快速将积压的消息给解决掉。

参考链接

https://www.jianshu.com/p/88a4da652e23

https://blog.csdn.net/qq_41701956/article/details/103276267

1
https://gitee.com/JavaInterviewHub/JavaInterview.git
git@gitee.com:JavaInterviewHub/JavaInterview.git
JavaInterviewHub
JavaInterview
JavaInterview
main

搜索帮助