以下是本消息队列系列文章的传送门:
1. 什么是消息队列
队列相信大家应该都不陌生,它是一种先进先出的数据结构,基本结构如下图。
队列在java中已经实现了各种各样的队列了,那为什么还需要消息队列MQ(Message Queue)这种中间件呢?我们可以先尝试思考一下消息队列存在的意义,它能满足我们项目中的什么需求,下面我会继续补充。
消息队列可以简单理解为,我们把想要传输的数据放到队列中(其结构与普通队列是一样的)
消息队列我们将把数据放入到队列的那一方叫做生产者
;将从消息队列中取数据的一方叫做消费者
。
2. 为什么要使用消息队列
以下我将通过几个简单的场景来简单说明我们为什么使用消息队列。
2.1 解耦
假设我们现在有一个环境数据生成系统A,系统A通过调用系统B和系统C的接口来向系统B、C来发送数据,场景一图如下:
场景1
即系统A在有新的环境数据生成时,会通过调用B、C系统的接口来将生成的数据发送给B、C。写成伪代码如下所示(此处不考虑用观察者模式实现解耦并实时通知观察者的情况):
public class SystemA {
// 系统B和系统C的依赖
SystemB systemB = new SystemB();
SystemC systemC = new SystemC();
// 系统A独有的数据
private String environmentData= "data";
public void doSomething() {
// 通过调用接口发送数据
systemB.SystemBNeed2do(environmentData);
systemC.SystemCNeed2do(environmentData);
}
}
这种情况下,会有以下问题:
- 如果某一天系统B不需要这些数据了,让系统A的负责人将发送数据给他们的那一部分代码给改了,即注释掉
systemB.SystemBNeed2do(environmentData);
; - 有新的系统D需要这些数据了,需要增加调用D系统接口来发送数据的代码;
- 同时,系统A还需要考虑如果其余系统B、C、D的系统如果挂了怎么办,要不要重发。
从上面的场景可以发现,系统A与其他三个系统高度耦合了。我们试想如下场景二,系统A将最新的环境数据信息放到MQ中,哪个系统需要就可以订阅这个消息消费。如果某个系统不需要这些数据了,就取消对MQ消息的消费即可。这种场景下,A系统完全不需要去考虑将数据发送给谁,也不需要去维护之前发送数据的代码,不需要考虑其他系统是否调用成功、失败超时等情况。
场景2小结:
通过这种模式消息发布与订阅的模式,使得系统A与其他系统解耦。
2.2 异步
其实我仔细斟酌了一下异步与解耦的场景,发现其实异步与解耦是有一定关系的,我们使用同样的场景,只是我们考虑的面不同,场景三如下图所示。但我们现在考虑的面是时间效率。
场景3
假设系统A产生环境数据需要100ms,调用系统B、C、D的接口分别需要300ms、300ms、300ms,那么在类似场景一的设计方式时,这次请求所需要耗费的时间为100+300+300+300 = 1000ms = 1s。请求会随着需要环境数据的系统越来越多而使得请求响应越来越慢,这是用户不能忍受的,即类似我们去请求一个系统服务,我们主要想要的服务只是系统A生成的环境数据服务,而系统A这个时候却因为其它系统调用而使得这个服务请求响应过慢。
而如果是类似于如下场景时,我们的响应时间仅仅是产生消息的时间100ms。
场景4
小结:
- 同步场景下,整个请求需要耗时1s;
- 异步场景下,请求只需要100ms。
2.3 削峰/限流
我们接着考虑如下场景,假设我们的系统A有个促销功能,大促销期间并发量较大,我们假设每秒可能有5k个请求。如下图。
场景5
一般的MySQL,每秒能处理2k个请求就差不多了,如果每秒5k个请求同时处理的话,可能会导致MySQL不可用,导致整个系统崩溃,用户也就没法使用MySQL了。但一过了促销期,每秒请求只有百来个,这个时候对系统几乎没有任何压力。
如果使用MQ,每秒5k个请求写入MQ,A系统可以根据自身的处理能力来决定每秒拉取的请求数。这样下来,在高峰期时系统A也不会挂掉。
场景6
这个短暂的高峰期积压是完全OK的。(消息队列是支持吞高吐量的,为此不用担心这个量的问题,同样,我们可以暂不考虑MQ挂掉的情况)。
系统A按照它能处理的速率来处理请求。
小结:
通过将请求放在支持高吞吐量的MQ中来到达削峰/限流的效果。
2.4 其它应用场景
- 日志处理:将大量日志存储到消息队列中(一般采用分布式消息队列kafka),解决大量日志传输的问题。其中,消息队列负责日志数据的接收,存储和转发;
- 消息通讯:点对点通讯或聊天室通讯。
3. 使用消息队列会有什么优缺点
优点其实就是上文中谈到的,在特殊场景下的应用好处,解耦、异步与削峰等。
系统可用性:
系统因为依赖与MQ消息队列这个服务,若这个服务崩溃了,那么我们的整个系统将不可用。为此往往我们都是通过集群/分布式部署来实现MQ高可用的。
系统复杂度:
我们将数据写到消息队列上,就有可能会存在数据丢失的情况。以及我们如何保证消息没有被重复消费等问题。
一致性问题:
A系统将请求写入到消息队列后就返回请求成功了,假设在多机部署的时候,系统B、C写库成功,假设D写入失败了,这种情况下就会产生数据不一致的问题。
4. 后语
总的来说,虽然引入消息队列有很多好处,但是也得针对它的缺点来引入其它技术方案和架构来规避这些问题,所以我们应该按照我们的实际需求与场景,来选择我们的技术方案。
本人水平有限,难免有错误或遗漏之处,望大家指正和谅解,欢迎评论留言。
网友评论