第一章 消息中间件介绍
1.MQ概述
MQ全称是Message Queue,消息的队列,因为是队列,所以遵循FIFO 先进先出的原则,它是一种跨进程的通信机制,用于上下游传递消息。
在互联网架构中,MQ是一种非常常见的上下游“逻辑解耦+物理解耦”的消息通信服务。
消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削峰等问题实现高性能,高可用,可伸缩和最终一致性架构;
使用较多的消息队列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ等。
例如下图所示:
原Client1是与Client2直接交互的,
升级后的架构中间加入MQ后,Client1将消息直接发送到MQ,MQ接收成功后便直接返回给Client1成功状态,Client1可以再去处理其他消息。
这时候MQ就成为了一个中转站,MQ接收到消息后可以通过推的方式,推送给订阅自己的Client2,也可以通过Client2主动拉取的方式,获取到Msg消息,进行处理。
处理完成后给MQ一个ACK通知,此消息就会从MQ中删除掉。
2.为什么使用MQ
既然有这样的中间件产品,那必然会有他的用武之地。一般大家会从异步处理,应用解耦,流量削峰这些维度去分别举例介绍。本文举一个例子来涵盖这些内容。
2.1 举个栗子:
如上图,这是一个实际的支付系统中的一个场景,用户通过支付服务扣款100,首先会调用一次账户服务扣除 用户余额100,(此步骤交互图中已省略),扣款成功后,立马会返回给用户支付成功的状态,但程序还会去处理扣款后的剩余事项,例如给商户上账、调用风控事后监控、发消息给用户、调用结算服务记录清分流水等等。
如果我们使用上图中的系统交互,首先调用账户、再调风控、调结算、调消息。。。最后返回给用户支付成功。
会存在什么问题?
- 系统严重耦合 - 这几个系统中存在关联关系,且执行也有先后顺序,任何一个系统的变化都有可能导致整条链路出现问题,如果再新加一个库存服务呢,又会对整体产生影响。
- 调用时间太长 - 用户支付动作,在用户侧关注的是什么?是有没有支付成功,即用户的钱被扣了就可以告诉用户支付成功,至于后面的一系列动作,跟用户不再产生关系,用户也不必关注。等全部执行完,再告诉用户执行时长可想而知。。
- 并发能力太弱 - 在业务系统开展活动时,需要一个高并发的系统支撑,我们无法迅速去提升,因为整体的并发能力完全依赖与所有的服务,而有的服务可能并发性就不是很好,或提升很慢,例如账户服务[核心诉求是记对账],这样整体的并发无法保证。
2.2 优化栗子
经过优化,我们中间引入MQ中间件,实现了异步化,支付服务不再和后面的任何服务进行耦合关联,只关注于消息的投递。消息投递成功后,即返回给前端用户支付成功。
带来的变化:
1. 系统解耦 -系统间不耦合,任何系统的变化不对整体链路产生影响
2. 异步提速 - 支付动作整体的耗时花费在了 用户与支付服务交互,后面的执行时间不再累计到整体链路中
3. 流量削峰 - 针对用户高并发的场景只要优化支付服务即可,而后续的服务无法承载如此高的并发,这时候MQ便起到一个中转暂存的作用,产生了一个削峰的作用。既提高了用户的并发能力,又保护了后端相应的低并发服务。
当然,还有一些其他的场景中都是会存在MQ的身影,不再一一列举了,但凡在使用过程中我们遇到一些想使用异步的场景,都可以考虑是否可以引入MQ。
3.MQ使用缺点
凡事有利就有弊~,事物都有两面性。再看下引入MQ后的系统缺点:
- 系统可用性降低 -系统引入的外部依赖越多,越容易挂掉。本来 A 系统调用 BCD 三个系统的接口就好了,ABCD 四个系统好好的,没啥问题。硬加个 MQ,MQ 挂了,整套系统就崩溃了,风险很大。因此,系统可用性降低。
- 系统复杂性增加 -要多考虑很多方面的问题,如何处理消息丢失的情况?如何保证消息传递的顺序性?如何保证消息没有重复消费?
- 数据一致性问题- A 系统处理完了直接返回成功了,人都以为这个请求就成功了;但要是 BCD 三个系统那里,BD 两个系统写库成功了,结果 C 系统写库失败了,这数据就不一致了。所以消息队列实际是一种非常复杂的架构,引入它有很多好处,但是也得针对它带来的坏处做各种额外的技术方案和架构来规避掉,系统复杂度提升了一个数量级,也许是复杂了 10 倍。
但这些缺点也可以通过一些手段去克服,总归引入MQ还是利大于弊的,要设计好整体的方案。