在我们的日常开发中,基本都会用到消息队列(MQ),使用场景都大同小异,MQ的技术选型可能不同,比如RabbitMQ、RocketMQ、Kafka、Pulsar等。
今天我们来讲讲为什么需要消息队列,消息队列主要解决的是什么问题。
异步处理
我们在面试中,应该都被问过一个经典却没有标准答案的问题:如何设计一个秒杀系统?这个问题可以有多个设计方案,但大多数都离不开消息队列。
秒杀系统需要解决的核心问题是,如何利用有限的服务器资源,尽可能多地处理短时间内的海量请求。我们知道,处理一个秒杀请求包含了很多步骤,例如:
- 风险控制;
- 库存锁定;
- 生成订单;
- 短信通知;
- 更新统计数据。
如果没有任何优化,正常的处理流程是:App 将请求发送给网关,依次调用上述 5 个流程,然后将结果返回给 APP。
对于这 5 个步骤来说,能否决定秒杀成功,实际上只有风险控制和库存锁定这 2 个步骤。只要用户的秒杀请求通过风险控制,并在服务端完成库存锁定,就可以给用户返回秒杀结果了,对于后续的生成订单、短信通知和更新统计数据等步骤,并不一定要在秒杀请求中处理完成。
所以当服务端完成前面 2 个步骤,确定本次请求的秒杀结果后,就可以马上给用户返回响应,然后把请求的数据放入消息队列中,由消息队列异步地进行后续的操作。
处理一个秒杀请求,从 5 个步骤减少为 2 个步骤,这样不仅响应速度更快,并且在秒杀期间,我们可以把大量的服务器资源用来处理秒杀请求。秒杀结束后再把资源用于处理后面的步骤,充分利用有限的服务器资源处理更多的秒杀请求。
可以看到,在这个场景中,消息队列被用于实现服务的异步处理。这样做的好处是:
- 响应快:可以更快地返回结果;
- 提升吞吐量:减少等待,自然实现了步骤之间的并发,提升系统的并发。
流量控制(削峰)
继续说我们的秒杀系统,我们已经使用消息队列实现了部分工作的异步处理,但我们还面临一个问题:如何避免过多的请求压垮我们的秒杀系统?
我们的设计思路是,使用消息队列隔离网关和后端服务,以达到流量控制和保护后端服务的目的。
加入消息队列后,整个秒杀流程变为:
- 网关在收到请求后,将请求放入请求消息队列;
- 后端服务从请求消息队列中获取 APP 请求,完成后续秒杀处理过程,然后返回结果。
秒杀开始后,当短时间内大量的秒杀请求到达网关时,不会直接冲击到后端的秒杀服务,而是先堆积在消息队列中,后端服务按照自己的最大处理能力,从消息队列中消费请求进行处理。
这种设计的优点是:能根据下游的处理能力自动调节流量,达到“削峰填谷”的作用。
但这样做同样是有代价的:
- 增加了系统调用链环节,导致总体的响应时延变长。
- 上下游系统都要将同步调用改为异步消息,增加了系统的复杂度。
那还有没有更简单一点儿的流量控制方法呢?如果我们能预估出秒杀服务的处理能力,就可以用消息队列实现一个令牌桶,更简单地进行流量控制。
实现的方式也很简单,不需要破坏原有的调用链,只要网关在处理 APP 请求时增加一个获取令牌的逻辑。
服务解耦
消息队列的另外一个作用,就是实现系统应用之间的解耦。我们举一个电商系统的例子来说明解耦的作用和必要性。
我们知道订单是电商系统中比较核心的数据,当一个新订单创建时:支付系统需要发起支付流程;风控系统需要审核订单的合法性;客服系统需要给用户发短信告知用户;经营分析系统需要更新统计数据;
这些订单下游的系统都需要实时获得订单数据。随着业务不断发展,这些订单下游系统不断的增加,不断变化,并且每个系统可能只需要订单数据的一个子集,负责订单服务的开发团队不得不花费很大的精力,应对不断增加变化的下游系统,不停地修改调试订单系统与这些下游系统的接口。任何一个下游系统接口变更,都需要订单模块重新进行一次上线,对于一个电商的核心服务来说,这几乎是不可接受的。
所有的电商都选择用消息队列来解决类似的系统耦合过于紧密的问题。引入消息队列后,订单服务在订单变化时发送一条消息到消息队列的一个主题 Order 中,所有下游系统都订阅主题 Order,这样每个下游系统都可以获得一份实时完整的订单数据。
无论增加、减少下游系统或是下游系统需求如何变化,订单服务都无需做任何更改,实现了订单服务与下游服务的解耦。
其它场景
以上就是消息队列最常被使用的三种场景:异步处理、流量控制和服务解耦。
当然,消息队列的适用范围不仅仅局限于这些场景,还有包括:作为发布 / 订阅系统实现一个微服务级系统间的观察者模式;连接流计算任务和数据;用于将消息广播给大量接收者。
最后
消息队列的使用场景还是比较广泛的,可以提高系统的响应时间和吞吐量,进行流量的控制,也可以对上下游服务的解耦,但是同时也带来了其他问题:
- 延迟问题;
- 增加了系统的复杂度;
- 可能产生数据不一致的问题。
所以我们说没有最好的架构,只有最适合的架构。
文档信息
- 本文作者:yindongxu
- 本文链接:https://iceblow.github.io/2022/05/04/%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E7%94%A8MQ/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)