导引

在系统架构当中,我们常常会用到消息中间件,但你可能会疑惑,什么是消息中间件?为什么需要引入消息中间件?消息中间件又有什么样的优缺点?

那么,让我们一起来探索消息中间件的奥秘吧~

什么是消息中间件?

消息中间件利用高效可靠的消息传递机制进行平台无关的数据交流,并且基于数据通信进行分布式系统的集成。通过提供消息传递和消息排队模型,可以在分布式环境下扩展进程间的通信。简单来说消息中间件就是一方与另一方进行消息交流管理与存储的一种技术,也可以看做是一个容器。

为什么需要消息中间件?

这个问题就需要我们先了解消息中间件常见的使用场景,然后再联系自己的项目进行分析。那我们先来了解一下消息中间件几种常见的使用场景。

1、系统解耦

我们打个比方说你一个系统A,那么我们的系统A会生成一个核心数据,然后我们在下游有一个系统B和系统C,而且系统B和C都需要这个由A产出的数据。

那么我们系统A直接调用系统B和C的数据接口发送数据给A和B即可。

完整过程如下图所示:

探索MQ消息中间件的奥秘-RadeBit瑞安全

但是要是现在有了系统D、E、F等等的其他多个系统慢慢的都需要这个由A生产的核心数据呢?

就如下图所示:

探索MQ消息中间件的奥秘-RadeBit瑞安全

一般来说,一个庞大的项目系统往往会拆分出几十个或者几百个的子系统,然后每一个子系统有对应着许许多多的服务,这样的系统与系统之间拥有着错综复杂的关系网络。

在这样的一个系统当中,如果某个系统生成出一份核心数据,那么下游可能有许许多多的其他系统都需要这份核心数据来实现各种业务逻辑。

在此时此刻,你如果要是采取了上图一样的系统架构模式,那么负责系统A的朋友绝对会各种需求给烦死。

我们可以设想一下,先来一个人找A负责人,要求他发送一个数据给新的系统H,那么系统A的负责人需要修改代码然后再那个代码里面加入调用新系统H的流程。

然后一会儿系统B系统老旧需要下线了,告诉系统A的负责人说:不需要给我发送数据了,我要下线了。接着系统A同学再次修改代码,不再给系统B发送数据。

那么我们不妨再设想一下,如果下游某个系统突然宕机又会怎样呢?系统A的调用代码里是不是会抛异常?那系统A的同学会收到报警说异常了,结果他还要去关系是下游哪个系统宕机了。

因此,当我们在系统架构设计中全部采用这种系统耦合的方式,那么在某些场景下是不太合适的,因为系统耦合度太严重了,并且相互耦合起来并不是核心链路的调用,而是一些非核心场景进行调用而导致了系统的耦合,这样会严重影响上下游系统的开发和维护效率。

因此在上述系统架构中,就可以采用MQ中间件来实现系统解耦。A就把一份核心数据发送到MQ,下游的系统如果需要,那么自己去消费即可,不需要就取消消费,如下图所示。

探索MQ消息中间件的奥秘-RadeBit瑞安全

2、异步调用

我们假设有一个系统调用的链路,假设A调用B,耗时20ms。B调用C,耗时200ms。C调用D,耗时2s。

就如下图所示:

探索MQ消息中间件的奥秘-RadeBit瑞安全

那么问题来了,当用户的一个请求非常的慢,完成一个链路耗费2220ms,也就是2多秒的时间。但是事实上,链路中A调用B,B调用C,这两个步骤也要220ms。只是因为引入了C调用D,所以才导致最终执行时间为2s多,这样一来直接把链路调用性能降低了,这就是导致链路执行过慢的罪魁祸首。

我们其实可以想一下,那么是不是我们可以把D从中拿出去做异步调用呢?其实很多的业务场景是可以允许异步调用的。

我来举一个例子,当用户在点外卖的时候,用户选择菜品下单付款,这个时候系统进行账户的扣款、创建订单以及通知商家。然后,系统平台需要寻找骑手并派发订单,然而寻找骑手是需要使用复杂的算法进行调度的,在这个过程当中是相当耗时的。但是如果我们的系统晚几秒完成调度任务是没有太大的问题的,因为在实际情况当中,我们没有必要在客户下完单之后立马进行派单调度。

所以我们不就可以把骑手送餐这一步从这个系统链路中提取出去,我们可以把它做成异步化,不用管到底需要延迟秒还是几分钟,只需要我们在一定时间内调度骑手去送餐即可。这样一来我们就可以让用户点单的速度和效率加快?在订单支付成功后,系统直接完成创建订单、扣款、通知商家即可,这个过程可能也就几百毫秒而已。然后让我们后台异步化进行派单配送,而且这个步骤不影响用户的下单。当然我不是说外卖平台技术架构的实现就是这样的,我只不过举个例子而已。

所以上面的链路也是这样的,如果我们的业务流程允许异步化,那么我们就考虑一下把C对D的调用抽取出去做异步化,不要把他放在链路里边依次调用。这样一来我们的实现思路就可以这样:A -> B -> C,这样我们就只需要耗费220ms就可以了。然后C发个消息给MQ中间件,由D消费到消息后慢慢的异步执行耗时2s的业务处理。我们就通过这种方式把执行性能提升了10倍。

整个过程,如下图所示。

探索MQ消息中间件的奥秘-RadeBit瑞安全

3、流量削峰

我们假设你有一个系统,在平时正常工作的时候每秒也就几百个请求,我们把这个系统部署在4核心8G内存的机器上,业务的正常处理都是一切正常的,用户的几百请求/秒也都是是可以轻松应对的。但是系统在高峰期一下子出现每秒几千请求的情况,在这个时候我们会选择买十多台机器来应对这个高峰吗?

探索MQ消息中间件的奥秘-RadeBit瑞安全

那如果这个高峰也就每天几十分钟,然后就又恢复平常流量了,那么,我们多买的几台机器不就又浪费了?系统在大部分时候每秒几百请求,我们多买的机器也就只是每天应对几十分钟的时间,这就造成了资源极大的浪费。

探索MQ消息中间件的奥秘-RadeBit瑞安全

但是我们如果就部署一台机器,在我们系统遇到高峰时期,那么我们的服务一定就会瘫痪。此时我们就可以用MQ中间件来进行流量削峰。我们在所有机器前部署一层MQ,在日常消费使用的过程中可以轻松应对,到了高峰时期,一下子来的每秒千次请求我们可以把他积压在MQ里边,然后我们让那台机器慢慢的进行消费和处理即可。然后我们等高峰期过了然后再让机器消费MQ里面堆积的数据即可。

探索MQ消息中间件的奥秘-RadeBit瑞安全

这就是一个很典型的MQ常见使用场景案例,我们用有限的机器资源来承受住高并发场景,如果我们的业务场景允许我们异步削峰,那么高峰期在MQ堆积的数据在高峰期过后,就可以消费完成。

使用消息中间件又有什么样的缺点?

我们在前面讲了许许多多的使用场景和好处,那么我们在引入了消息中间件后又会带来什么样的坏处呢?

1、系统可用性降低

首先就是系统的整体可用性会降低,我们来举个例子:比方说我们在一个核心链路里,A -> B -> C,然后C是通过MQ异步调用D。

如下图所示:

探索MQ消息中间件的奥秘-RadeBit瑞安全

这样子看起来虽好,但我们又会联想到一个问题就是如果MQ中间件宕机会怎么样?只要MQ宕了,那么就会使得你的核心链路断掉。原本在不引入MQ的情况下,我们只是简单的系统间调用,而我们再引入了MQ中间件,那么我们就会多出一个依赖,影响我们的可用性。因此,当我们引入MQ中间件,我们就必须要了解MQ怎么样去部署,怎么样保持服务的高可用性。而且我们还需要考虑到当我们的MQ中间件宕机后,又应该有怎样的机制去应急处理这种情况。

系统稳定性降低

这还是上面那张图:

探索MQ消息中间件的奥秘-RadeBit瑞安全

大家有没有看到一个问题,在这个链路中除MQ中间件宕机的这种情况外,这个系统还有可能存在其他问题。比方说,C发给MQ一个消息,然后可能因为网络问题或者其他问题导致这个消息没有传到MQ,然后D就没有获取到C的消息。这样有可能会使得D没完成自己的业务流程,在这个时候可能会发生系统崩溃、数据遗失等等的bug,严重影响用户体验。又或者说C给MQ中间件发送消息,然后因为某些原因重复发送了相同的数据给MQ中间件,这又该怎么办?这有可能会使得D将C发送的数据执行2次添加插入,产生许多的垃圾数据,导致各种各样的问题。或者说如果D宕机无法消费MQ中的消息,那么许许多多的消息在MQ堆积了起来,这时又如何去解决?即便是D恢复正常了,这也需要花费许多时间去消费堆积的数据。因此,当我们引入了MQ中间件的同时也会带来许许多多的问题,而且一旦某个环节出现问题,就会导致整个系统出错。这就需要我们采用一些技术方案去解决MQ 带来的问题,这些技术方案,在这里就不展开讲述了,有兴趣的同学可以去了解学习一下。

分布式一致性问题

引入MQ中间件,还会有可能出现分布式一致性问题。那么,什么是分布式一致性问题呢?我们来举个简单的例子,比方说C在执行自己的本地数据库已经成功,然后C给MQ发送了一个消息,而且D同时也消费到了。结果不幸的是,D操作自己本地数据库失败了,那么这个时候应该怎么办?C成功,D失败,这又会导致系统整体的数据不一致,所以在这种情况下又需要我们去使用可靠的消息最终一致性的分布式事务方案来保障。

总结

那么最后,让我们来做一个简单的小结。

我们在了解了什么是MQ中间件之后,想必大家对于MQ中间件也有了一定的认识。在一些业务场景下,我们可以使用MQ中间件去解决一些问题,但是我们同时也要了解到在我们引入MQ中间件后会给系统带来怎样的影响,思考我们如何去解决后面可能出现的问题。MQ中间件虽然优点很多,但我们同时也要了解到它的不足之处,我们在认识到MQ带来的问题之后,我们需要出一系列的解决方案来保证自己的系统的高可用、高可靠性以及保证数据的一致性。