本文最初发布于 RedMonk。
在流式传输等替代技术大行其道的时代,消息队列为何能经久不衰?对于这个问题,我们得先回顾一下消息传递的发展历程,才能更好地理解它的现状。当我开始研究消息队列时,我一直在努力梳理出一段连贯的历史,而本文就是要解决这个问题。我已经发表了本系列文章的开篇之作。在那篇文章中,我将消息队列定义为 “分布式系统中的主流范式,[它] 必不可少,为人所熟知,也因此也变得越来越乏味”。在这篇文章中,我将重点介绍下,我们是如何得出队列占据主导地位这一结论的,以及这种地位在 Kafka 时代是否能够得以延续,并特别关注下,哪些主要的科技公司及行业垂直领域预示着消息队列的崛起。
最近,我与消息传递领域的两位传奇人物、RedMonk 的老朋友讨论了消息队列的历史,他们分别是:IBM 研究部门杰出工程师和创新领导者 Andy Stanford-Clark,以及微软消息传递和实时智能服务首席架构师 Clemens Vasters。下面是 Stanford-Clark 说的一段话,我把它作为这篇文章的开头,是因为本文要讨论基于队列的消息传递历史,而这段话可以说明软件工程师开发消息队列的初衷:
因此,我认为最关键的是异步消息传递。...... 显然,人们将计算机连接在一起并将应用程序连接在一起已经有很长一段时间了,但采用的一直都是同步方式。换句话说,一个应用程序在开始发送信息之前,必须做好接收信息的准备。它们必须在收到整个信息并确认后,才能继续发送。我们曾把这比作巨型喷气式飞机的机翼...... 当飞机在跑道上起飞时,机翼会轻微扇动...... 因为如果机翼是僵硬的,就会被折断,所以机翼必须有一定的弹性。而在两个应用程序之间设置队列或弹性连接,就能防止它们僵硬。这样就能让它们有一点弹性。如果一个程序还没有完成前一个程序的工作,那么消息就会进入队列,该程序可以继续它当前的工作。等它准备好了,它就会从队列中获取消息。这样的“交换”系统使得整个工作得以继续进行,而不必完全同步。
事实证明,这是处理复杂情况的唯一出路,应用程序、人员、传感器以及企业中的所有事物。因此,基于队列进行消息传递的想法应运而生。IBM 推出了 MQ 系列,后来成为 WebSphere MQ。从字面上讲,它是基于队列的点对点消息传递。如果将一条消息放入队列,那么应用程序 A 就可以认为该消息已实际传送到应用程序 B,这是因为我们有这种确保一次且仅有一次的传送方式,所以一旦将消息放入队列,就可以安全地忘记它,不会有重试,也不会有恢复,消息已经发出去了,它会在某个时候传送到应用程序 B。这是一个非常强大的概念。
消息队列之所以能满足现代分布式系统对灵活性的要求,很大程度上就是因为它是异步的。不过,这个范式是怎么形成的呢?在深入探讨更细微的历史之前,我们有必要划清消息队列和其他消息传递模式之间的界限。如果你已经非常了解这些领域之间细微的技术区别,可以跳过这一节。
在计算机科学中,消息传递有 4 种核心模式:
一对一和请求回复 / 请求 - 响应: 这是一种(典型的)同步通信方法,一个系统发送请求,另一个系统回复所请求的数据。这种双向信息交换模式类似于打电话,需要发送方等待回应。该模式在 HTTP 调用等客户端 - 服务器架构中应用广泛。“Sync over async “或 ”sync/async "指的是同步系统和异步系统之间的通信,常用于实现企业应用集成(EAI)。在这种场景下,响应需要依赖于速度缓慢的聚合函数或人工工作流。
一对多 Pub/Sub: 发布 - 订阅(Pub/Sub)模式,有时也称为 推送技术,与消息队列模式关系密切,通常是面向消息的中间件(MOM)系统的一部分。它允许发布者将消息归类,然后由订阅者接收。与直接传递消息不同的是,订阅者只接收自己感兴趣的消息,而且不知道哪些发布者参与其中。这种模式常用于面向消息的中间件系统(如 Java 消息服务 JMS),具有更好的可扩展性,但会带来数据结构和格式耦合方面的复杂性。
数据流: 通过数字编码信号序列传输信息,通常组织成数据包。这种模式近年来非常流行,因为它通过简化数据流的处理过程,消除了从数据库中提取数据所需的时间和资源开销。在后流行病时代,数据流技术蓬勃发展,因为它可以成功地实现远程和分布式团队所需的实时协作。
消息队列: 消息队列能在无服务器和微服务架构中实现服务间的异步通信。它可以暂存消息,直到消息被处理和删除。消息队列有助于分离复杂的处理过程,处理缓冲或批处理,更高效地管理有波动的工作负载。消息队列模式与 Pub/Sub 模式密切相关,通常是更大的面向消息的中间件系统的一部分,许多系统(如 JMS)的 API 都支持这两种模式。
好了,我们现在已经大致介绍了消息队列的功能,并且对主要的消息传递模式做了一些说明,所以可以开始讨论这项技术的历史进程了。
Stanford-Clark 指出,IBM 是消息队列的先驱,最早的例子是 IBM 在 1965 年发布的队列远程通信访问方法(QTAM)(另见 IBM Robert M. Winick 于 1969 年在 ACM 发表的论文:远程通信环境中的 QTAM 控制和处理 )。QTAM 先后被远程通信访问方法(TCAM) 和 虚拟远程通信访问方法(VTAM) 所取代,其中 VTAM 是为 IBM System/360(S/360)大型机开发的内置通信访问方法(见上图)。在很大程度上,美国国家航空航天局(NASA)的阿波罗计划资助了 S/360。也就是说,在早期,美国太空计划在推进基于队列的远程通信子系统方面发挥了不小的作用。不过,在所有为面向消息的中间件这个领域做出贡献的行业中,银行和航空公司在异步消息传递的兴起中扮演了重要的角色,特别是在保证事务型消息传递系统这一范式中。
银行和航空公司特别需要有保证的消息传递这一核心功能,而这通常是通过两阶段提交协议来实现。该协议可以确保原子性,因为在金融和预订交易系统中,插队、出错、重复等会导致灾难性的后果。IBM 凭借其事务处理设施(TPF)大型机操作系统开创了这一技术的一个经久不衰的版本。该系统运行 TPF 旅客预订应用程序(PARS 或国际版 IPARS)。IBM 的航空消息传递软件 TPF、PARS 和 IPARS 都是在 60、70 年代开发的,都是 IBM 的专有技术。企业客户仍然在积极使用这些消息传递应用程序,尤其是 IBM z/TPF。
对于有保证的消息传递系统,另一个可能更为成熟的应用场景是金融机构。长期以来,实体银行和交易所一直以分布式的方式在广大地区运营。各机构之间需要用协调一致的方式进行通信和集成,以便每个地方的数据都能及时更新。几十年来,有许多网络技术推动了这种通信方式的发展。这方面的例子比比皆是,但有两个最为突出:一个是在 20 世纪 20 年代股市崩盘中发挥了显著作用的电报打印纸带技术;另一个是 Michael Lewis 在《闪电男孩》(Flash Boys)一书中记录的用于高频交易的光纤线路。快速、准确地将信息从一个地方传送到另一个地方是金融业的传统,它影响了各行各业的网络系统。
如今,银行使用的计算机化消息传递系统需要确保消息代理已经接收并处理了所有发出和输入的数据,这一点毋庸置疑。尽管关于如何做到这一点的技术细节不在本文的讨论范围内,但他们在业务层面采取的措施却非常值得注意。为了保证性能,金融业都采用了基于消息传递的开放标准,其中包括环球银行间金融电信协会(SWIFT)、金融信息交换协议(FIX) 和 金融产品标记语言(FpML)。除了创建业务协议和报文格式外,几十年来,为了确保这些机构的消息传递流程顺利运行,许多软件供应商纷纷挺身而出。值得注意的是,在上世纪 90 年代,许多银行都使用 IBM MQ 进行集成,并使用 TIBCO Rendezvous(RV) 进行发布 / 订阅,因此让我们先来了解一下这些厂商。
IBM MQSeries(后更名为 WebSphere MQ(2002 年)和 IBM MQ(2014 年))于 1993 年首次推出,用于在分布式系统中实现安全的点对点和 Pub/Sub 消息传递。Brian Wilson 是 IBM 美洲自动化集成技术销售主管。他在 2023 年的一篇文章中写道:
IBM MQ 广泛应用于各行各业,但在银行和金融服务市场的应用场景可能是最重要的...... 全球 100 强银行中有 98 家使用了 IBM MQ。你会发现,85% 的财富 100 强企业都在使用 IBM MQ。而且,运输、物流、零售、保险、电信、医疗保健等行业也有大量的企业在使用 IBM MQ。举例来说,一家大型航空公司,如果 IBM MQ 出现故障,飞机就无法起飞。
在企业 Pub/Sub 消息传递领域,IBM MQ 一直占据着主导地位,但 25 年前,它曾与后起之秀 TIBCO 展开过激烈竞争。1997 年,Tugrul Firatli、Vivek Ranadive 和 Vijay Tella 创建了 TIBCO(信息总线公司)。这是一家实时消息服务公司,也是 Pub/Sub 的早期先驱。Ranadivé是在 Teknekro 软件系统公司任职时创建的 TIBCO。该公司是 Teknekron 公司企业孵化器的一部分,1993 年被路透社收购。上世纪 90 年代末,TIBCO 在金融业特别是华尔街交易大厅的自动化方面发挥了重要作用。
自 2000 年代以来,TIBCO 已经从消息代理过渡到企业应用集成(EAI),以满足客户完成必要业务任务的需求,例如,在不泄露任何数据的情况下将 Oracle 后端与零售银行应用程序集成。TIBCO RV 采用基于主题(subject)的 EAI 消息传递模型,其中,系统按 Topic 订阅特定的 RV 主题,而 Rendezvous Daemon(RVD) 负责接收来自这些订阅者的消息。EAI 的兴起是消息队列发展史上的一个重要转折点,因为它一直到今天都还很流行。如今,消费者可以从多个 EAI 平台中进行选择,其中包括 APPSeCONNECT、Azure Integration Services、Boomi、IBM App Connect Enterprise、Informatica、Workato 和 Oracle Cloud Infrastructure Integration Services。
这让我想到了消息传递的开放性问题,Redis 最近的重新授权和 Valkey 分支无疑是我们 RedMonk 人最关心的话题。从一开始,开源消息队列系统和开放协议的前景就令开发人员、IT 运维人员和管理员兴奋不已。消息传递问题的复杂性使得任何系统的成功都离不开生态系统、集成和标准化。高级消息队列协议(AMQP)是一个特别成功的开放标准,在 2000 年代中期一经推出就大受欢迎。2003 年,John O'Har 在伦敦摩根大通(JPMorgan Chase)开发了 AMQP。随后,许多金融机构都加入了该工作组,共同推进该协议的发展。Pieter Hintjens 一生编写了 30 多个协议,在创建异步消息传递库 ZeroMQ 之前,他也曾为 AMQP 的设计做出过贡献。
RabbitMQ 是一个流行的 AMQP 实例,由 Rabbit Technologies 公司(后被 VMware 收购)于 2007 年开发。它是该协议被大量采用的主要原因。虽然在促进消息传递方面,VMware 所起的作用或许值得单独写成一部历史书,但在这里,我们只需要指出,Rabbit Technologies 创始人兼首席执行官、后来的 VMware 高级总监 Alexis Richardson 等人以及整个 Spring 团队对队列技术的发展起到了至关重要的作用。
关于 RabbitMQ,让我们再多聊一些,因为就消息传递开放性方面的需求和挑战来说,它提供了令人信服的论据。时至今日,RabbitMQ 仍然无处不在,因为它运行良好,而且是开源的。RabbitMQ 曾被用在 Linux Debian 中,后来成为 NASA 计算平台 Nebula) 的基础,后者是 OpenStack 的两个创始项目之一。Heroku 和 Cloud Foundry(由 VMware 开发)也使用了它。在很大程度上,RabbitMQ 的流行归功于其强大的社区。RabbitMQ 团队发起了一场成功的开发营销活动,并在采用率方面获得了不小的回报。事实上,开发人员的热情常常被认为是促成 RabbitMQ 在 2010 年被收购的因素。
MQTT,25 岁生日快乐!请欣赏 MQTT 共同发明人 @andysc——@IBM 研究部杰出工程师和创新领导者——即将在 @redmonk MonkCast 播客中播出的关于消息传递历史和演变的谈话。pic.twitter.com/xIlNKsUKmI —— Kate Holterhoff 博士(@KateHolterhoff)2024 年 10 月 22 日
在过去的 25 年里,消息传递已经转移到了边缘应用场景。从石油管道到心脏起搏器,到处都需要银行在上世纪 90 年代所要求的防故障功能。1999 年,Stanford-Clark 公司开发了一种轻量级的开放式消息队列协议 MQTT,针对的正是这种情况(MQTT 25 周年生日快乐!)。MQTT 中的 MQ 来自 IBM 的 MQSeries,后更名为 IBM MQ。IBM MQ 广泛应用于工业自动化领域,而 MQTT 则是为远程物联网量身定制的。销售托管 MQTT 平台的 HiveMQ 就在其面向工业、交通和汽车客户的营销中利用了这一用例。
从以 AMQP 和 MQTT 为代表的开放消息传递协议的发展历史中可以看出,开放性、互操作性、社区支持和开发人员的热情推动了队列技术在各行各业(从金融到物联网)的广泛应用并延续至今。它们的成功以工程社区不断变化的需求和偏好为基础。它们为可靠、可扩展系统所依赖的队列技术的下一个重要阶段——数据库 -——奠定了基础。
如果你觉得银行和航空公司所要求的有保证的消息传递听起来很像 ACID,那就对了。不仅数据库越来越多地采用 MQ 类型的特性,原子性、一致性、隔离性和持久性等也已成为这些领域共享的特性。数据库可以写入、删除、更新和移动记录。同样,在消息传递中,应用程序也需要能够写入、删除、更新和移动消息。我们看到,由于存在这种重叠,Redis 正在成为一种流行的消息队列。FoundationDB 还将 ACID 遵从性作为这种分布式数据库的核心——一种可以无误运行的能力,而 Anthesis 则将这种能力转到了质量保证和测试领域。如果从 CAP 定理的角度来考虑数据库,那么一致性、可用性和分区容忍性等问题都可以直接映射到消息传递和 MOM 设计用来处理的复杂问题上。
我之所以强调数据库表现得像消息队列一样,是因为越来越多的分布式系统工程师抱怨,并非每个系统都需要消息队列所带来的复杂性或可扩展性,他们正在寻求更简单的替代方案。随着 Kubernetes 在 2014 年的推出,基于微服务的实现开始在 DevOps 领域占据主导地位,而微服务曾一度与消息队列紧密联系在一起,因为它们通常需要进行服务解耦和异步通信。初期对于微服务的大肆宣传,导致许多公司采用 Kafka、NATS 和 AMQP 等消息代理系统作为服务间通信的主干。由于从设计上微服务(尤其是 Kubernetes 等环境中的微服务)就会经常出问题,所以消息代理系统有助于实现微服务消息传递的容错性。分布式系统从业者认识到,单体架构或更小的紧耦合系统可能是更合适的解决方案,同时还能避免管理分布式系统的开销(异步处理、错误处理和重试逻辑),以及向第三方支付的管理这些系统的费用。下面是 Hacker News 上一位用户的评论:
“有什么理由不能在 Postgres 中完成这些工作?”对于这个问题的答案,我的标准比 10 年前高了很多很多。
这些工程师认为,在许多情况下,KISS 原则都倾向于使用直接 HTTP 调用、数据库支持的队列、用于内存消息处理的 Redis 以及分布式日志,而不是消息队列。
在和队列类似的数据库中,日志已成为一种特别流行的范式。例如,Twitter 的分布式键值数据库 Manhattan 就利用分布式日志来提供持久、有序的事件记录,并允许以不同的方式重放和处理这些记录。另一个流行的选项是 Apache BookKeeper 项目。该项目于 2017 年与 DistributedLog(DL)合并。BookKeeper 是雅虎工程师开发的,当时是作为 Hadoop HDFS NameNodeone 的高可用解决方案。这些服务旨在维护记录序列(称为日志或日志流)。日志非常适合需要实时处理事件流的应用,因此也就不奇怪,流服务领域无可争议的巨头 Apache Kafka 也将消息存储在不可变的分布式日志(称为主题) 中。
21 世纪前 10 年,和数据库拥有类似属性(持久、一致、可无限期保留数据)的服务开始崛起,它们提供消息传递功能并称之为流(streaming),其中最著名的是 Apache Kafka。Kafka 采用 “分布式提交日志 ”设计(4),是一个 Pub/Sub 消息传递系统,由 Jay Kreps、Neha Narkhede 和 Jun Rao 在 LinkedIn 工作时开发,并于 2011 年初作为 Apache 孵化器项目的一部分开源。Kafka 是流式传输领域名副其实的大象。Confluent(由 Kreps、Narkhede 和 Rao 于 2014 年创立)、Redpanda 和 WarpStream(今年被 Confluent 收购)等多家公司都在使用它。事实上,那些追踪消息传递领域最新进展的人可能会说,我们正处于 Kafka 时代——这并不是因为其他类型的消息传递变得不那么流行了,事实远非如此,而是因为利用流可以赚钱。关于这一点,我们将在下一篇文章中继续讨论。
围绕 Kafka 是否符合数据库定义的争论,就像在问软件工程师,一个大头针的头上能站下多少个微服务一样。从业人员普遍认为,对于大多数用例来说,将 Kafka(普通或托管)用作数据库是不明智的。比这个学术问题更有趣的是,为了满足 Neha Narkhede、Gwen Shapira 和 Todd Palino 在 《Kafka 权威指南》 一书中所描述的 “持续数据流”需求,消息传递是如何发展的:
我们的想法是,与其像关系数据库、键值存储、搜索索引或缓存那样专注于保存成堆的数据,不如专注于将数据视为持续进化和不断增长的数据流,并围绕这一想法构建数据系统,甚至是数据架构。(xiii)
流服务的用例包括像 Uber 这样的共享乘车应用程序和像 Netflix 这样的视频点播服务。这些服务改变了我们的日常生活,如今,它们已经得到了广泛的认可,对于那些受益于它们的人来说更是如此。Alexis Richardson 认为,“关于流是否是一个独立的用例,还存在很多争议,而今天,随着 RabbitMQ 和 NATS 对流的支持,这已经成为桌面上的赌注”。到设备的流式传输服务和实时数据处理服务正在飞速发展,这个就不再那么有争议了。流数据的兴起与放弃消息传递保证的趋势不谋而合。企业已经意识到,在很多问题上,原子性并没有低延迟那么重要。Netflix 将大量的数据从服务器逐个像素地传输到我的电视机上,保证这些数据的事务性是不可能的,也是不必要的。虽然银行已经创建了 FAST 协议 来调整 FIX 以实现流式传输,但这只是众多用例中的一种。事实上,2011 年出现的 WebSocket 协议因其双向性,在字节流领域正显现出一定的发展势头。该协议尤其适用于实时视频游戏和推送。
有时候,开发人员会对 Kafka 是否支持队列感到困惑,比如这位 Redditor 就问:“将 Kafka 用作消息队列合适吗?”就像决定将 Kafka 用作数据库一样,从业人员的共识是,这取决于单个的用例。一些经验丰富的分布式系统工程师认为,在规模比较大的时候,这不是一个好的解决方案。这些对 Kafka-as-MQ 持怀疑态度的人建议实施 Apache Pulsar 或 RabbitMQ 等替代消息传递系统,同时他们也指出,Confluent 软件工程师、消息传递、事件流和事务处理技术专家 Andrew Schofield 撰写的 Kafka 改进提案(KIP-932,Queues for Kafka)证明了他们的看法。这些反对者正确地 指出,Kafka 与队列不同:
消费者组为组内成员分配分区的方式提供了顺序和可扩展性,但也确实导致了消费者组中消费者数量与分区数量之间的耦合。Kafka 用户往往不得不 “过度分区”,以确保有足够的并行消费来应对峰值负载。
如果实现了的话,那么“Queues for Kafka”就可以使用 Kafka 主题通过合作消费来实现类似队列的行为,这样就不再需要单独的队列了。但是,KIP-932 是否意味着队列和 Kafka 在协议层面的分离呢?考虑到 KIP 引人联想的标题,我急切地想从 Schofield 那里进一步了解关于 Kafka 与队列的关系:
Kafka 是一个日志,我们要做的就是在 Kafka 主题之上提供另一种访问模式,其行为更像是一个队列。原因主要在于,在 KIP 发布之前,开发人员在编写消费 Kafka 主题的应用程序时会使用消费者组。消费者组可以决定你能够运行多少个消费者,有多少个分区,因为你可以在消费者和分区之间进行强分配。因此,它非常适合大规模事件流。在那种情况下,你希望应用程序以非常快的速度拉取并处理所有记录。但如今,很多人都把 Kafka 当作一种更通用的消息传递技术。有了这个 KIP,我们将为应用程序提供一种更灵活一些的消费方式,即更多地按消息消费,而不是给我整个分区,对吗?从本质上讲,它只是让共享变得更容易。所以,这不是一个真正的队列,但它能让你编写队列式的应用程序,这才是重点。
队列与类队列实现之间并没有一个清晰的界限,但工程师们坚持认为,严格的队列和近似的队列都有其价值。Kafka 时代发展迅猛,越来越多的企业客户采用了事件流技术,但尽管如此,队列仍将继续存在。
消息队列已经走过了漫长的道路,它们不会很快消失。当我向 Stanford-Clark 问及 MQTT 后续的发展趋势时,他很乐观:
我们可以为 MQTT 的下一个 25 年举杯,因为它的受欢迎程度丝毫没有放缓的迹象。事实上,在许多方面都呈指数曲线。
队列仍然是解决许多分布式系统问题的重要工具,如果你觉得队列的受欢迎程度有所下降,那反映的并不是队列的发展颓势,而是这种模式已成为一种可靠、成熟的技术。虽然作为一种可行的替代方案,流的分布式日志和事件驱动架构备受瞩目,但从很多方面来看,围绕消息队列的讨论有所减少,反而表明了消息队列已经成功地为世界上一些最大型的系统提供了支持。
声明:IBM、谷歌、微软、甲骨文均为 RedMonk 的客户。
声明:本文为 InfoQ 翻译,未经许可禁止转载。