vlambda博客
学习文章列表

消息队列哪些常见的面试题

阅读文本大概需要3分钟。

0x01:为什么使用消息队列

  • 解耦

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

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

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

  • 异步

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

      一般互联网类的企业,对于用户直接的操作,一般要求是每个请求都必须在200ms 以内完成,对用户几乎是无感知的。采用MQ,那么A系统连续发送3条消息到MQ队列中,假如耗时5ms,A系统从接受一个请求到返回响应给用户,总时长是3+5=8ms,对于用户而言,其实感觉上就是点个按钮,8ms以后就直接返回了。

  • 削峰

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

      一般的 MySQL,扛到每秒2k个请求就差不多了,如果每秒请求到5k的话,可能就直接把MySQL给打死了,导致系统崩溃,用户也就没法再使用系统了。但是高峰期一过,到了下午的时候,就成了低峰期,可能也就1w的用户同时在网站上操作,每秒中的请求数量可能也就50个请求,对整个系统几乎没有任何的压力。

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

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


0x02:使用消息队列有什么缺点

  • 系统可用性降低

      系统引入的外部依赖越多,越容易挂掉。本来你就是 A 系统调用 B、C、D三个系统的接口就好了,人 A、B、C、D四个系统好好的,没啥问题,你偏加个MQ进来,万一MQ挂了咋整,MQ一挂,整套系统崩溃的,你不就完了?如何保证消息队列的高可用,可以点击这里查看。

  • 系统复杂度提高

       硬生生加个MQ进来,你怎么保证消息没有重复消费?怎么处理消息丢失的情况?怎么保证消息传递的顺序性?头大头大,问题一大堆,痛苦不已。

  • 一致性问题

       A系统处理完了直接返回成功了,人都以为你这个请求就成功了;但是问题是,要是 B、C、D 三个系统那里,B、D两个系统写库成功了,结果C系统写库失败了,咋整?你这数据就不一致了。

       所以消息队列实际是一种非常复杂的架构,引入MQ有很多好处,但是也得针对它带来的坏处做各种额外的技术方案和架构来规避掉,做好之后,你会发现,系统复杂度提升了一个数量级,也许是复杂了10倍。但是关键时刻,还是得用的。


0x03:消息队列如何选型

  • 如果消息队列不是将要构建系统的重点,对消息队列功能和性能没有很高的要求,只需要一个快速上手易于维护的消息队列,建议使用RabbitMQ。

  • 如果系统使用消息队列主要场景是处理在线业务,比如在交易系统中用消息队列传递订单,需要低延迟和高稳定性,建议使用RocketMQ。

  • 如果需要处理海量的消息,像收集日志、监控信息或是埋点这类数据,或是你的应用场景大量使用了大数据、流计算相关的开源产品,那Kafka是最适合的消息队列。

每一个消息队列都有自己的优劣势,需要根据现有系统的情况,选择最适合的消息队列!


0x04:如何保证消息不被重复消费

      要保证消息不被重复消费,需要保证消息消费时的幂等性,保证了幂等性,重复消费了也不会造成系统异常。幂等性,通俗的说无论重复请求多少次,都得确保对应的数据是不会改变的。

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

怎么保证消息队列消费的幂等性呢?

  • 如果你拿个数据要写库,你先根据主键查一下,如果这数据都有了,你就别插入了,update 一下好吧。

  • 如果你拿到这个消息做 Redis的set的操作, 无论set几次结果都是一样的,set操作本来就算幂等操作。

  • 如果你不是上面两种场景,那做的稍微复杂一点,准备一个第三方介质,来做消费记录。以redis为例,你需要让生产者发送每条数据的时候,里面加一个全局唯一的 id,只要消费过该消息,就将id和消息数据写入redis,消费者开始消费前,先根据这个id查redis中有没有消费记录,消费过了,就别处理了,保证别重复处理相同的消息即可。

  • 数据库的唯一键约束也可以保证不会重复插入多条,因为重复插入多条只会报错,不会导致数据库中出现脏数据。

参考:https://www.cnblogs.com/midoujava/p/11488925.html



往期精彩



01 

02 

03 

04 

05 

关注我

每天进步一点点

喜欢!在看☟