一、消息队列是个什么东西?

在使用一门新技术之前我们肯定要搞明白这是个什么东西。消息队列这个词想必大家都很熟悉,不管你用没用过,你应该听过吧?即便你没有听过消息队列,那队列你应该听说过,所以在学习什么是消息队列之前我们先来说一下什么是队列(queue)。队列可以说是一个数据结构,可以存储数据,如下图,我们从右侧(队尾)插入元素(入队),从队头获取元素(出队),先入先出。

消息队列里放整个对象 消息队列用来干嘛_java

对于这样一个数据结构想必大家都不陌生,Java中也实现了好多队列。例如,创建线程池时我们需要一个阻塞队列,JDK的Lock机制也需要队列。

了解了队列之后,我们来看一下什么是消息队列,消息队列就是我们常说的MQ,英文叫Message Queue,是作为一个单独的中间件产品存在的,独立部署。

消息队列里放整个对象 消息队列用来干嘛_数据_02

二、为什么要用消息队列呢?

引入一个新的技术产品,肯定是要考虑为什么要用它呢?消息队列也不列外,说到为什么要用,还真是因为它能在某些场景下发挥奇效。例如:解耦,异步,削峰,这三个词你也听说过吧,那下面就就从这三个好处出发,讲讲到底什么是解耦,异步,削峰。

2.1 解耦

解耦都不陌生吧,就是降低耦合度,我们都知道Spring的主要目的是降低耦合,那MQ又是如何解耦的呢?如下图所示,系统A是一个关键性的系统,产生数据后需要通知到系统B和系统C做响应的反应,三个系统都写好了,稳定运行;某一天,系统D也需要在系统A产生数据后作出反应,那就得系统A改代码,去调系统D的接口,好,改完了,上线了。假设过了某段时间,系统C因为某些原因,不需要作出反应了,不要系统A调它接口了,就让系统A把调接口的代码删了,系统A的负责人可定会很烦,改来改去,不停的改,不同的测,还得看会不会影响系统B,系统D。没办法,这种架构下,就是这样麻烦。

消息队列里放整个对象 消息队列用来干嘛_开发语言_03

而且这样还没考虑异常情况,假如系统A产生了数据,本来需要实时调系统B的,结果系统B宕机了或重启了,没调成功咋办,或者调用返回失败怎么办,系统A是不是要考虑要不要重试?还要开发一套重试机制,系统A要考虑的东西也太多了吧。

那如果使用MQ会是什么样的效果呢?如下图所示,系统A产生数据之后,将该数据写到MQ中,系统A就不管了,不用关心谁消费,谁不消费,即使是再来一个系统E,或者是系统D不需要数据了,系统A也不需要做任何改变,而系统B、C、D是否消费成功,也不用系统A去关心,通过这样一种机制,系统A和其他各系统之间的强耦合是不是一下子就解除了,系统A是不是一下子清爽了很多?

消息队列里放整个对象 消息队列用来干嘛_数据_04

2.2 异步

同步/异步大家都知道吧,举个例子,你早上起床后边吃早饭边看电视(异步),而不是吃完饭再看电视(同步)。在上述例子中,没有使用MQ时,系统A要调系统B、C、D的接口,我们看一下下面的伪代码想一下是不是这样

//系统A中的代码 
Data newData = productData();//系统A经过一些逻辑处理后产生了数据,耗时200ms  
Response responseB = callSysB(newData);//系统A调系统B接口发送数据,耗时300ms 
Response responseC = callSysC(newData);//系统A调系统C接口发送数据,耗时300ms 
Response responseD = callSysD(newData);//系统A调系统D接口发送数据,耗时300ms

这样系统A的用户做完这个操作就需要等待:

200ms+300ms+300ms+300ms=1100ms=1.1s

点个按钮等一秒多,用户体验得多差啊,客户可能就会因此而流失掉。

假设使用了MQ呢?系统A就只需要把产生的数据放到MQ里就行了,就可以立马返回用户响应。伪代码如下:

//系统A中的代码 
Data newData = productData();//系统A经过一些逻辑处理后产生了数据,耗时200ms 
writeDataToMQ(newData);//往MQ里写消息,耗时50ms

这样系统A的用户做完这个操作就只需要等待200ms+50ms=250ms,给用户的感觉就是一瞬间的事儿,点一下就好了,用户体验提升了很多。系统A把数据写到MQ里,系统B、C、D就自己去拿,慢慢消费就行了。一般就是一些时效性要求不高的操作,比如下单成功系统A调系统B发下单成功的短信,短信晚几秒发都是OK的。

2.3 削峰

削峰是什么意思?大家都知道对于大型互联网公司,典型的就是电商,时不时的搞一些大促,流量会高于平时几十倍几百倍...例如,平时下单也就每秒一二十单,对于现有的架构来说完全不是事儿,那大促的时候呢?每秒就有可能举个例子是5000单,如果说下单要实时操作数据库,假设数据库最大承受一秒2000,那大促的时候一秒5000的话数据库肯定会被打死的,数据库一挂导致系统直接不可用,那是多么严重的事情。

所以在这种场景下使用MQ完美的解决了这个问题,下游系统下单时只需要往MQ里发消息,我的订单系统可以设定消费的频率,比如每秒我就消费2000个消息(在数据库的可承受范围),不管你下游系统每秒下多少单,我都保持这个速率,既不会影响我订单系统的数据库,也不影响你下游系统的下单操作,很好的保护了系统,也提高了下单的吞吐量。

我们都知道,大促也就几分钟的事,往多了说是几个小时吧,咱就说4个小时吧,每秒5000,4小时7200W单往MQ里写,订单系统每秒消费2000单,大促过后,MQ里会积压4320W个消息,剩下的就慢慢消费呗。当然了,大促的时候肯定会临时申请加机器的,每秒消费不止2000。

这就是削峰,将某一段时间的超高流量分摊到更长的一段时间内去消化,避免了流量洪峰击垮系统。