什么是消息中间件?消息队列?

1,201 阅读9分钟

入门简介

消息中间件和消息队列是同一个概念的两种名称,是分布式系统中的一个重要组件。

消息队列做了什么:

当高并发的请求冲向系统后,传统情况下,一个请求在大量微服务集群的系统中,很可能存在复杂业务——A服务调用B服务,B服务调用C服务,C服务调用D服务......的一条调用链,我们已经使用服务熔断降级限流等手段解决了服务雪崩的问题,但这里又存在着新的情况。

A服务需要执行100ms,B服务100ms,C服务100ms,D服务100ms,那么这样一个调用链长了之后,导致用户请求,执行了很久,等待时间过长。

但是,带来的问题不仅仅是用户体验不良,1s钟的等待看起来也是可以接受的。

可是,1s钟才能处理的请求十分消耗性能资源,很容易导致高并发情况下系统的崩溃,虽然我们可以采用熔断降级限流的手段来控制,但是仅仅因为某些服务处理复杂顶不住高并发,就把整条链路中其他简单业务也停用限流熔断很明显不合理。

此时引入一个消息队列的概念。

我们将一次请求中传递的信息称之为消息,有消息就需要有响应。

传统情况的系统,每一个消息要在等到请求处理完并且得到响应,才算是一次请求完成。

而消息队列的概念就是请求发送进了系统,我不关心他有没有没有处理完成,就先行返回响应,后台再去处理请求。

如果处理者能够忙得过来我们就很快处理请求,忙不过来没关系,请求放进消息队列排着队等着你处理,不给你过大的压力,不消耗过量的系统资源,不紧不慢,慢慢处理,等高并发过去之后,前面的简单业务休息了,处理消息队列还没有停下来。

为什么会有消息队列这个概念?

上面的内容还是没明白?
没关系!

先来看一个形象的举例

就好比,某学校现在有一个表格需要统计,每个人都要提供个人信息来填表 (请求),班长来汇总 (处理请求的服务),但是班长要花30s钟的时间接受一位同学发来的填表信息汇总到表格里 (请求的处理过程)

情况一(没有消息队列):

假如没有互联网还使用了最笨的处理方式 (没有消息队列),同学需要来到班长的身边告诉班长需要的信息 (发送请求) ,等待班长填完告诉你可以了 (请求的响应),才可以离开 (一次完整的请求)

上述,只来了一位同学,班长可以快速响应,30s处理完就搞定了,时间不长可以接受。

但是......

现在同一时间来了10个同学 (稍微提高了并发),只有一个班长,排在最后的同学要等5分钟 (系统在有能力处理的时候不会坐视这样的情况发生),于是学习委员、体育委员、...等十个委员此时跑来帮忙 (系统中空闲资源全部参与处理并发),又30s解决了所有的问题 (高资源损耗的处理了所有请求)

乍一看也没事,合理的调用了空闲资源完成了任务

(什么?有人问委员就这么多,那同一时间来100个怎么办呢,这里被 班主任Hystrix/Sentinel/Gateway)要求每30s只能来十个) (系统同一时间只能处理有限请求,利用之前的知识,我们可以在这里做到限流,虽然解决了问题,但损失了流量)

但是......

同一时间有10个同学的情况一直持续了两小时

此时首当其冲的问题是,所有班长委员都跑来做这件事情了 (资源倾斜),导致其他事情的处理也变慢了 (对系统中其他的请求也产生了影响)

另外一个更严重的问题,一直保持着填表的班长委员们心态爆炸,填表的手也开始发抖 (性能损耗)。原本30s填完一个也变成了1分钟填完一个,但依旧30s来十个同学,班长等人都表示干不动了需要休息 (请求积压,系统濒临奔溃最终宕机熔断)

这样一个过程就是当前面临的问题,虽然可以通过进一步限流的方式,或者升级系统的性能,来缓解系统的压力,但是限流就会损失流量,升级性能代价高昂,有什么办法可以更好的解决问题呢??

休息之后班长发现,其实,这样的并发再坚持一小时就过去了,我可不可以不着急这么做事累坏了自己,找空闲时间我再去处理??

消息队列站了出来!!

什么是消息队列再看看情况二:

后来,班长觉得这么做事情实在是太蠢了,为什么每个人都要排着队等着呢?

想了个办法,叫来了学习委员**(引入消息队列)**

让同学们,把需要的信息写在纸上,只需要把纸扔给学习委员 (请求发送给消息队列),学习委员就马上告诉你ok,我会处理的,你可以走了 (不着急处理业务先快速响应)

然后把新的纸条丢进一摞纸条的最后面 (队列),让纸条排着队,任你纸条再多 (并发再高),我不紧不慢 (只要我不处理,并发就跟我没关系o(∩_∩)o ),按照正常的速度填表 (像平常一样处理业务)

类似丢个纸条给我这种简单事情的处理,你就是同时来一百个同学给我丢纸条,我也顶得住,这里并发不可怕 (也就是消息队列接收消息放进队列存起来慢慢给服务处理这个过程撑住并发很容易),比填表这种并发容易处理太多了

这就是消息队列做了最主要的一件事。

  • 把请求的消息放入队列中排队执行
  • 将高并发的请求积压下来慢慢处理
  • 这样做虽然收到的请求是高并发的
  • 但我处理的中间隔着一层消息队列
  • 处理的速度还在我的可控范围之内
  • 一方面先返回响应表示请求收到了
  • 我会处理,不影响调用者做其他事情
  • 另一方面我按照请求处理者的节奏给他正常负担的请求量慢慢处理
  • 请求者不等,处理者不累,请求最终也处理完了,皆大欢喜

适用场景

这种架构适合一些对响应速度要求不高的业务

典型实例一、

很典型的一个例子,订电影票火车票等,第一时间并没有告知你有没有订到票,而是后台处理完票库存下单锁定等等一系列问题之后,通过短信等形式再通知你成功与否

典型实例二、

电商系统中,下完订单,很明显支付模块一定是高响应速度的,付完钱必须马上看到钱没了,和订单马上变成已支付状态,这个支付服务一般不能使用消息队列

可是支付前后,后台还有很多要处理的逻辑

比如下图红色部分,如果这些处理都同步来做,根据统计多个子步骤执行完成,加起来需要1~2秒

img

这样的话如果把所有的东西糅杂在一起,点击支付一起处理,那高并发情况下很难做到快速响应,还会导致很多系统性能问题

所以下单、支付、支付后续,被分开处理了

你在下单的时候,遇到高并发的情况,例如双十一,可能会使用消息队列,比如下单后,不会马上成功下单等待支付,只告诉你订单提交成功,请稍后刷新查看订单状态,后台排着队处理订单,依旧是检查库存锁定等等该做的操作全部处理完只剩下最后一步支付,处理完之后订单状态从待处理变成待支付,下一步支付只需要简单的一个支付操作即可让订单变为已支付,然后继续后续请求放进消息队列,调用后续的发货、仓储、运输等模块,用户再慢慢的看到订单状态变成代发货、运送中、已收货等等。

总结

这里只帮助大家理解概念,明白为什么需要引入消息中间件/消息队列

没有做深入的讲解

其实消息中间件引入后,还存在大量的问题

解决了前面问题的同时,增加了系统的复杂性,带来了新的问题

比如如何保证发出去的消息在传递中不会丢失不会出错?

又如何保证消息中间件这个系统万一宕机数据不会丢失?

又如何保证消息处理者宕机或者其他情况下不会重复消费同一条消息?

这里只是消息中间件的理念,那么落地实现了这些理念的实际产物有哪些?各自又有哪些优缺点?

这些问题需要各位继续通过系统的学习来深入了解

推荐尚硅谷阳哥的ActiveMQ作为入门是一个不错的选择(链接在文章末尾)

据我了解大多数主流还是使用RabbitMQ

可以先ActiveMQ入门,其他的等用到哪个再学习

也可以选择直接去学习RabbitMQ

但不管哪个消息中间件,总体的理念都是相通的,学懂一个其他再学起来也都很简单,不攻自破

如何学习我也就不多提了,相信各自都有自己的方式

经过本文的理解,打通了任督二脉再去学习,相信是可以事半功倍的