作者:minimiai_559 | 来源:互联网 | 2023-09-11 19:43
13项目中为什么要使用消息队列
学习从来无捷径,循序渐进登高峰。
—— 高永祚
引言
上个章节把Redis夺命连环问掰扯完,面试还没有结束,消息队列同样是面试中必问的,分布式构建三把斧:缓存+异步+数据分组,从这节开始进入异步解决方案-消息队列
生活中的队列:超市买菜排队付款,出去玩排队上飞机。
(图片来源:https://medium.com/)
计算机系统中的队列:
凡是可以“排队”去做的事情,都可以使用消息队列。网上买东西同样也需要“排队付款”,但是有人说,我点确认付款后马上就显示成功了,没感觉到排队呀?其实在后台系统中是排了,只不过排队的时间对于人来说有点短,可能1-2秒就结束了,但是对于计算机来说,这1-2秒的时间很长了。大型分布式系统建设中,消息队列主要解决应用耦合、异步消息、流量削锋等问题。实现高性能、高可用、可伸缩和最终一致性架构。是大型分布式系统不可缺少的中间件。消息发布者只管把消息发布到 MQ 中而不用管谁来取,消息使用者只管从 MQ 中取消息而不管是谁发布的。这样发布者和使用者都不用知道对方的存在。
Web应用程序毫无疑问有大量的代码执行HTTP请求/响应周期的一部分。这适用于更快的任务耗费数百毫秒内或更少。然而,有些处理,还需要耗时更多甚至最终会是一两秒钟缓慢的同步执行,在如此长时间的调用流转中,肯定有一些调用是可以不同步的,如下单送积分,用户下单是最主要的,送积分的操作可以异步去做,订单支付成功给用户的短信通知,返回支付订单进入下一环节更重好,短信通知可以异步去发送,为了应对诸如此类的异步操作,消息队列这门技术应运而生。
1.面试官:你在项目中使用过消息队列吗?公司使用什么消息框架。
我:
我了解的常用消息队列中间件框架
- ActiveMQ:由 Apache 出品的一款开源消息中间件。
- RabbitMQ:Rabbit科技有限公司开发,使用Erlang语言开发的开源消息队列系统,基于AMQP协议来实现。
- RocketMQ:阿里巴巴自主开发,是淘宝内部的交易系统使用了淘宝自主研发的Notify消息中间件,使用MySQL作为消息存储媒介,可完全水平扩容。
- Kafka:最初是由领英开发,并随后于2011年初开源,并于2012年10月23日由Apache Incubator孵化出站。
这其中Kafka天生就是分布式设计队列的消息理论上可以分摊到无数服务节点上。同等硬件环境下 Kafka 的性能大大超过传统的 ActiveMQ、RabbitMQ等MQ实现,这是 Kafka 炙手可热的重要原因,我们使用的就是Kafka搭建的集群。
Tip:这只是一个开始,面试官可能顺着你的话题开始聊Kafka了,Kafka的特性我在后面单独讲一节,继续展开讲消息队列基础知识。
2.面试官:哪些业务场景使用了消息队列?
问题分析:
在我刚刚工作接触编程的时候,做的项目都是单机部署,一套框架走天下,根本没有机会使用到消息中间件,真正参与大型互联网公司的分布式系统后豁然开朗,原来系统之间通讯是这么轻松愉快。所以如果要拿到大厂offer,至少要了解一个消息框架。
我:
“一百字的定义,不如一句话的实例”
我在公司的核心部分负责的是订单核心系统(具体什么订单系统自行yy一个)。
这是一个完整的外卖订单流程。
从普通用户的角度来看,一个外卖订单从下单后,会经历支付、商家接单、配送、用户收货、售后及订单完成多个阶段。
以技术的视角来分解的话,每个阶段依赖于多个子服务来共同完成,比如下单会依赖于购物车、订单预览、确认订单服务,这些子服务又会依赖于底层基础系统来完成其功能,总之就是一个订单从提单到完成,要经历一个非常庞杂的流程。
一个服务所需要处理的工作越少,其性能自然越高。可以通过将部分操作异步化来减少需要同步进行的操作,进而提升服务的性能。异步化有两种方案。
- 使用消息队列:异步操作通过接收消息完成。
- 使用多线程:将异步操作放在单独线程中处理,避免阻塞服务线程。
比如,日志收集,采集日志是系统中经常被需要的功能,业务系统(如订单系统)流量高,数量极大,响应时间要求高,如果想收集订单日志,收集日志的动作必然不可以影响订单主流程,采集日志过程就可以使用消息队列模型设计。
再比如,收银系统,确认收款成功,通过MQ通知给物流系统发货,这些独立系统之间的通讯都需要使用MQ。
再比如,消费积分,用户每消费一笔给用户增加一定积分,京东豆,信用卡积分,在一个正规大厂的架构设计中,可以100%的确定订单系统和积分/奖励系统是耦合在一起的,那怎么通知积分系统给用户加积分?这个时候MQ就登场了。
面试官打断了我…
3.面试官:那为什么不能使用线程池开启多线程呢,可同样达到异步的效果?
问题分析: 千万不能被面试官所迷惑,他是想要你给出使用消息队列有什么好处。
我:
这个就要从使用消息队列有什么好处说起了,我总结了以下几点:
使用消息队列有什么好处:
-
异步处理模式
比如上面说的日志收集功能,订单系统是消息发送者,一行代码发送一条日志消息而无须如何等待。消息发送者将消息发送到一条虚拟的通道(主题 或 队列)上,日志存储服务作为消息接收者监听该通道,这样做的好处就是收集日志不会阻碍主流程,日志积压在消息队列里,日志服务可以慢慢消化,也无需很高的性能。
至于为什么不使用多线程来做,这就是我要说的第2点。
-
应用系统解耦
订单服务和日志存储服务显然是两个服务,微服务时代,根据单一职责原则,订单系统只负责订单相关的工作,日志收集服务只负责收集日志,如果把这两件事通过线程池揉在一起,系统见界限就不明显了,做不到完全解耦。
-
流量削峰
当在线api接口在应对高峰流量时,比如“秒杀”互动流量激增时,如果日志服务接口处理能力有限,可以先将日志消息积压在队列里,后台慢慢处理,防止日志服务被打挂。
-
发布/订阅(Producer–consumer)
一条消息,可以广播给任意个收听方,Producer只负责发送消息 Message,Consumer可随意订阅 Message。就像广播一样,是系统之间跨机房跨机器通讯的主要手段。
场景:电商系统中,订单服务和支付服务往往是分开部署在不同机房,订单系统确认订单,给用户跳转到支付页面,支付成功后,支付系统广播一条 Message,用户xxx已经确认付款,订单订阅了此消息,将要订单状态改成已支付,物流系统收到支付成功消息开始发货,整个流程,系统之间可以通过MQ通讯。
面试官欣慰地笑了,似乎对我对回答很满意,我似乎也猜到他接下来要问什么了,关于“使用消息队列有什么缺点”,不如我一次痛快讲完。
我:
凡事都有两面性,不能考虑到消息队列的优点,分布式环境下,消息中间件的介入后会为系统提高复杂度,对应就会需要解决方案,使用消息中间件会带来什么问题呢?
使用消息队列有什么缺点:
- 消息丢失问题: 任何系统不能保证万无一失,比如 Producer 发出了10000条消息,Consumer 只收到了 9999 个消息,万有1失,Consumer 能否接受丢一条?如果是订单成功短信可以接受丢一条,就是有一个顾客没有通知到已经发货,但货还是发出去了,如果是支付系统,用户已经付款却因为消息丢失没有通知到订单或物流系统,那恐怕顾客要找你麻烦了。
- 消息重复问题:如 Producer 发出了10000条消息,Consumer 只收到了 10001 条消息,有一条是重复的,业务能否接受一条重复的消息,这个是作为系统设计者要考虑的问题。
- 消息的顺序问题:如 Producer 发送顺序是123,Consumer 收到的消息是132,要考虑消费端是否对顺序敏感。
- 一致性问题: 如消息丢失问题真的发生且无法找回,会造成两个系统的数据最终不一致,如果消息延迟,会造成短暂不一致。
面试官:小伙子你考虑地非常全面,关于消息队列的基础就问到这。(面试官彻底被我折服)。
Tip:
针对上面的问题都是消息中间件系统带来的常见困扰,是否需要解决完全依赖业务场景,如果问题对系统产生影响低可以接受,可以忽略,如何解决,每种消息中间件系统设计方案也有所不同,还需要考虑从业务上解决,如消息重复问题,如果 Consumer 不能接受重复消息,那 Consumer 的接口可以设计成“幂等”,这样就不怕重复消息给系统造成影响。
总结
本文主要针对消息队列的概念和使用做了讲解,消息队列同缓存一样,几乎是互联网系统建设中不可缺少的,面试也会被考察,通常会从以下几个方面进行考察:
- 你熟悉哪些消息中间件,各自有什么优缺点。
- 为什么使用消息中间件?
- 消息队列使用场景。
- 使用消息中间件有什么缺点,如何解决。