在这篇文章中,我们将深入探讨一种广泛使用的中间件:消息队列。
消息队列有着悠久的历史。它们通常用于不同系统之间的通信。图 1 通过将其与 Starbucks 的工作方式进行比较来说明消息队列的概念。
在 Starbucks,收银员接受订单并收款,然后他们将客户的名字写在咖啡杯上,交给下一步。咖啡师拿起订单和杯子制作咖啡。然后客户在柜台取走咖啡。这三个步骤异步工作。收银员只是将订单以咖啡杯的形式放下,不等待完成。咖啡师只是将完成的咖啡放在柜台上,不等待客户取走。
当你在 Starbucks 下单时,收银员接受订单并在杯子上潦草地写下你的名字,然后转向下一个客户。咖啡师拿起杯子,准备你的饮料,然后留给你取。这个过程的美妙之处在于每个步骤独立运作。它非常像一个异步系统。

这种异步处理(每个步骤不必等待前一个步骤完成)显著提高了系统的吞吐量。例如,收银员不必等待你的饮料制作完成就可以接受另一个订单。
现在,让我们将注意力转移到一个真实世界的例子:电商中的闪购。闪购可能会因用户活动激增而使系统紧张。许多策略被用来管理这种需求,消息队列通常在后台优化中发挥关键作用。
图 2 列出了简化的电商闪购架构。
步骤 1 和 2:客户向订单服务下订单。
步骤 3:在处理付款之前,订单服务预留所选库存。
步骤 4:订单服务向付款服务发送付款指令。付款服务扇出到 3 个服务:付款渠道、通知和分析。
步骤 5.1 和 6.1:付款服务向付款渠道服务发送付款指令。付款渠道服务与外部 PSP(付款服务提供商)对话以完成交易。
步骤 5.2 和 6.2:付款服务向通知服务发送通知,然后通知服务通过电子邮件或短信向客户发送通知。
步骤 5.3:付款服务向分析服务发送交易详情。

这里的一个关键要点是,在闪购期间无缝的用户体验至关重要。为了在高流量下保持服务响应能力,可以在多个阶段集成消息队列以确保最佳性能。
付款服务向三个下游服务发送数据用于不同目的:付款渠道、通知和分析。这种扇出方法就像有人在房间里大喊消息;需要听到的人就会听到。生产者只是将消息放在队列上,消费者以自己的节奏处理消息。
借鉴 Starbucks 的类比,就像收银员不等待咖啡制作一样,订单服务不等待付款完成。付款指令被放置在队列上,一旦完成就通知客户。
在闪购中,可能有数万名并发用户同时下订单。在满足热情客户和维持系统稳定性之间取得平衡至关重要。一种常见的方法是限制特定时间框架内的传入请求数量,以匹配系统的容量。多余的请求可能会被拒绝或被要求在短暂延迟后重试。这种方法确保系统保持稳定并且不会被压垮。对于通过的请求,消息队列确保它们被高效且按顺序处理。如果系统的一部分暂时滞后,订单不会丢失。它被保存在队列中,直到可以处理。这确保即使在压力下也能顺畅流动。
我们的设计在各种地方使用消息队列。整体架构与图 2 中呈现的简化版本不同。服务使用定义良好的消息接口相互交互,而不是紧密依赖彼此。每个服务可以独立修改和部署。每个组件可以用不同的编程语言开发。这为架构设计带来了灵活性。
由于服务是解耦的,我们可以根据需求独立扩展它们。每个服务可以以不同的容量服务,因此我们可以根据其计划的 QPS(每秒查询数)或 TPS(每秒事务数)进行扩展。
消息队列还可以用作用于存储消息的中间件。如果上游服务崩溃,下游服务总是可以从消息队列中获取消息进行处理。这样,恢复功能从每个服务中移出,成为消息队列的责任。
有时在处理流程中,我们需要批量数据以获得摘要。例如,当付款服务向分析服务发送更新时,分析服务不需要执行实时更新,而是设置一个滚动窗口来批量处理。批量处理是下游服务的要求,因此付款服务不需要知道它,只需将消息放入队列即可。
在闪购中,库存物品数量有限。例如,闪购只提供 10 部 iPhone,但有超过 10,000 名用户下订单。我们如何决定顺序?使用消息队列保存所有订单将有一个自然顺序:队列中的前 10 个将获得 iPhone。
在图 3 中,我们将所有内容整合在一起,服务通过消息队列连接并解耦。这样,架构可以实现更高的吞吐量。

本文为学习目的的个人翻译,译文仅供参考。
原文链接:Why Do We Need a Message Queue?。
版权归原作者或原刊登方所有。本文为非官方译本;如有不妥,请联系删除。