Skip to content

01 业界的主流消息队列是如何发展起来的?

你好,我是文强。

作为一个消息队列的老兵,我经常被团队的新同学或者客户的研发人员问:我希望在我们的业务中引入消息队列,应该选择哪一款合适?是不是用最近很火的Pulsar就好了,但是业务团队推荐用RocketMQ或RabbitMQ,而大数据团队推荐用Kafka,有没有什么选择的标准可以参考呢?

这个问题你之前一定疑惑过,可能也有了一套自己的选择标准,不过遇到这种情况,我都会先反问:你觉得消息队列是做什么的?

自从负责消息队列云服务之后,我接触了很多客户,发现基本90%以上的用户(大概率你也在其中),业务的数据量都不大,场景也不复杂。只用到了消息队列最基本的生产和消费功能,把消息队列当做缓冲分发的管道,基本不会用到消息队列的高级功能,比如死信队列、事务消息、延时消息等等。

所以,很多情况下,我们不需要很复杂的消息队列,有些时候甚至都不需要引入业界标准的消息队列产品。是不是和你之前的想法不太一致?今天我们就从这个有点反常识的观点开始讨论。

你真的需要标准消息队列吗?

我第一次用消息队列是在我负责游戏论坛开发的时候,有一个功能是需要把用户对帖子的评论和点赞数据发给下游分析。你可以想一下,如果你接到这个需求,会怎么做呢?

我的第一个想法就是在流程里面加一个函数,封装一下,再把数据通过 HTTP API 发送给下游,就完成了。

不过我上级选择了另外一套方案,引入Kafka,在主流程里面先把数据发送到Kafka,再让下游去消费数据。那时候Kafka还是0.8.*的版本,公司用得很少,我们团队也是第一次用,还特意找运维搭了套集群。

Apache Kafka是 LinkedIn 在2011 年贡献给了 Apache 软件基金会的分布式消息队列。主要满足大数据领域中的高吞吐量、低延迟场景。核心功能较为简单,只提供生产和消费,后来加了幂等和事务。采用通用分布式架构实现发布-订阅模型,能高效地处理海量数据。

当时我不太理解加Kafka的意义是什么。

因为我们的需求非常简单,只涉及生产和消费两个动作,而且现网一直在用Redis。当时我认为使用Redis的List实现Push/Pop就可以满足需求,效果是一样的,还省去Kafka的维护和学习成本。进一步,如果担心Redis的数据没做持久化丢失了,也可以通过MySQL的表,把数据insert进去,下游去select出来,也能实现Push/Pop的效果。

那为什么还是用Kafka呢?

当时有个业务背景:用户的回复会包含图片和表情等复杂结构数据,导致内容可能会特别长。另外遇到高峰时,回帖数量会特别多,短时间内可能有几千万的回帖,回帖的总内容很大。

Redis的问题在于数据都存在内存里,如果数据没有及时消费,就会打爆内存。而且因为回帖内容可能很大,在Redis的Value里存大的数据会有性能和稳定性问题。而MySQL在insert上是有性能瓶颈的,短时间内大量回帖,插入会特别频繁,性能就扛不住。

而Kafka就没有这个问题,作为一个消息队列,它的特点就是高吞吐、大消息、高并发、持久化,不会存在性能、功能、稳定性方面的问题。这就是我们选择Kafka的理由。

其实如果没有大消息和大流量等复杂场景,是可以选用非标准消息队列产品的。比如在用户状态审核的场景中,只需要向下游传递用户ID和审核结果,结构简单,数量有限。这时候选择非标准消息队列比如Redis和MySQL也是可以的。

所以,是否选择使用标准消息队列产品,取决于你的数据和业务场景的需求。 当数据量大、场景复杂后,才必须引入标准消息队列,因为它有高吞吐、持久化、长久堆积的特性。

既然用非标准消息队列也可以满足相应需求,那到底什么是消息队列呢?

从宏观上来讲,我会认为具有缓冲作用、具备类发布和订阅能力的存储引擎都可以称做消息队列。因为消息队列的最基本功能就是生产和消费,在发布订阅之上,扩展如死信队列、顺序消息、延时消息等高阶能力,并实现高吞吐、低延时、高可靠等特性,就成为了我们所熟知的功能齐全的标准消息队列。

现在业界都有哪些标准的消息队列呢?我们一起来梳理下。

业界都有哪些消息队列?

还是跟随我的时间线来看。后来我负责的广告业务用到了另一款消息队列 RabbitMQ,当时我最直观的感觉就是:它功能比Kafka多好多,支持延时消息、死信队列、优先级队列、事务消息等等,业务上也是因为要用到这些功能才选择的RabbitMQ。

RabbitMQ是2007年由国外一家叫做Rabbit的公司开源的,用Erlang 写成的消息队列。主要满足业务中消息总线的场景。特点是功能丰富,低流量下稳定性较高,基本具备消息队列所应该具备的所有功能。缺点是在大流量的情况下会有明显的瓶颈和稳定性风险。

当时开源的消息队列并不丰富。基于JMS协议发展出来的ActiveMQ 因为功能和稳定性问题,用的人比较少。Kafka刚开源不久,功能只有生产和消费。AMQP协议的 Erlang 实现 RabbitMQ,因为功能丰富,稳定性较高,成为主流选择。

ActiveMQ 项目最初是由 LogicBlaze 的创始人于 2004 年创建的,支持完成JMS规范的消息队列。因为生态、功能定位方面的原因,在国内用的人并不多。

AMQP是一个消息队列协议规范,它不是一款具体的消息队列。因为不同消息队列的访问协议是不一样的,导致不同的消息队列需要用不同的SDK访问,客户的切换成本很高。2003年,多个金融服务机构希望制定一个消息队列的协议规范,希望不同的消息队列的协议都根据这个标准实现,这样就可以不需要重复开发SDK,不同的应用程序之间的交互和切换可以更简单、更方便。这就是AMQP的由来。

不过开源选择少,并不意味着那时候没有好的消息队列可用,商业化的闭源的消息队列一直在蓬勃发展,但需要购买才能使用,而且不便宜。

比如微软1997年推出的MSMQ、AWS 2004年推出的SQS,而消息队列领域最牛的,某种意义上(从收入维度看)一直都是 IBM MQ,在金融、证券行业用得特别多,几乎一统天下。数据显示,到了2020 年 IBM MQ 每年在全球仍有近 10 亿美金的营收。现在国内各大云厂商消息队列的收入加起来,可能都不够IBM MQ的零头。

后来开源社区逐渐完善,阿里的 RocketMQ在2017年从Apache基金会毕业,Pulsar也在2018成为了Apache的顶级项目,开源社区生态逐渐繁荣,选择慢慢变多。

RocketMQ 在定位上和 RabbitMQ 很像,功能丰富,在业务消息中经常会用到。不过RocketMQ是在移动互联网浪潮下发展起来的,业务场景更加复杂,也支持更多功能,比如消息Tag、消息轨迹、消息查询等等。

除了功能层面,在架构和性能层面,RabbitMQ开发设计早,当时分布式的设计理念还不成熟,导致它在架构层面的设计存在较大的缺陷,遇到大流量、高并发的时候,容易出现集群不可用、网络分区等情况无法解决。而RocketMQ在分布式架构上实现得更合理优雅,在大流量、高并发的场景下表现优秀稳定。

RocketMQ是2013年阿里研发,2016年开源,可满足大规模微服务场景的消息队列产品。可以理解为是RabbitMQ的高可用、分布式升级版。功能丰富,基本可以满足业务消息场景下的所有需求。稳定性、数据可靠性方面的表现都较好。性能介于RabbitMQ和Kafka之间。

Pulsar和Kafka很像,主要定位在流领域,主打大吞吐的流式计算。但Kafka的功能比较简单,支持基本的发布订阅、幂等、事务消息。Pulsar在满足这些功能的基础上,也希望支持RocketMQ和RabbitMQ的功能,所以功能最丰富。

除了功能层面,在架构和性能层面上,Pulsar的架构设计比Kafka更符合当前云原生架构,它的定位是Kafka的升级版,主要解决Kafka当前的一些痛点问题,比如集群扩缩容慢、分区迁移需要Rebalance、无法支持超多分区等。性能目前没有特别大的差距。

不过Pulsar发展时间较短,架构较复杂,功能支持较多,当前阶段在稳定性上Kafka会比Pulsar好非常多。

Pulsar 2017年由 Yahoo 开发的消息队列系统。一开始定位是流计算领域,可以理解为Kafka的升级版,近期希望同时发展消息和流两个方向。其架构上的设计理念较为优秀,比如计算存储分离、弹性、多租户。在功能上目前正在追赶RabbitMQ/RocketMQ。性能层面,和Kafka没有明显的差异。但当前阶段的稳定性还需要提升。

这里总结一下,当前开源社区用的较多的消息队列主要有RabbitMQ、Kafka,RocketMQ和Pulsar四款。下面我整理了一张消息队列发展的时间脉络图,供你参考。

在开源社区发展的这段时间,国内大厂也一直在自研消息队列,比如阿里的RocketMQ、腾讯的CMQ和TubeMQ、京东的JMQ、字节的BMQ。只是发展程度不一样,有的开源了成为顶级项目,有的慢慢消亡了,有的仅限在公司内部使用。

虽然有这么多的消息队列,但它们发展的方向其实是一致的。

消息队列的发展脉络

结合我的个人经历,我们可以从消息队列的历史中抽象出两条交织的发展脉络:上层的需求变化和下层的架构演进。

  • 从需求发展路径上看,消息队列的发展趋势是: 消息 -> 流 -> 消息和流融合
  • 从架构发展的角度来看,消息队列的发展趋势是: 单机 -> 分布式 -> 云原生/Serverless

在90年代到21世纪初,以IBM MQ和AMQP为代表的消息队列主要满足业务上对消息的需求,即异步通讯、架构解耦。

2010年左右,移动互联网发展,大数据兴起,传统的消息队列在架构上无法满足大流量的吞吐需求,就发展出了以Kafka为代表的消息队列,主打大吞吐、大流量。我们也进入了分布式的时代,现在大家熟知的消息队列都是分布式的架构,所以会有分区、副本、一致性概念。

随着业务场景越来越复杂,业务消息的数据量也越来越大。基于开源AMQP的RabbitMQ在性能和架构上已经无法满足消息场景的需求,从而发展出了RocketMQ。

近几年随着云计算的发展、云原生和Serverless的理念兴起,在弹性、成本的驱动下,消息队列的架构往云原生/Serverless方向演变,简单来说,就是利用云上的弹性计算、存储等基础设施去实现架构的Serverless,按需使用、按量付费,最终达到使用端感受到的免运维、低成本。

基于云原生架构设计的Pulsar开始走向成熟,业界的MQ也出现了 计算存储分离、分层存储、多租户、弹性计算等概念

云原生/Serverless的发展趋势,你应该听得比较多了,“消息和流融合”的趋势可能有些陌生,我稍微解释一下。

什么是消息和流?

消息、流分开来看都比较好理解。

  • 消息就是业务消息,在业务架构(比如微服务架构)中用来作消息传递,做系统的消息总线,比如用户提交订单的流程。
  • 流,就是在大数据架构中用来做大流量时的数据削峰,比如日志的投递流转。

消息和流融合就是这两个事情都能做。不过为什么会有消息和流融合的这个趋势呢?

其实都是钱的原因。虽然消息队列是基础组件,但是功能比较单一,主要是缓冲作用,在消息、流的方向上,功能需求一直是相对固定的,细分的市场也都有领头组件,流领域目前是Kafka一家独大,消息领域的头部玩家,国外是RabbitMQ,国内是RocketMQ。

对MQ厂商来说,如果希望扩大产品份额或者新品抢占市场,就需要有 独特竞争力,核心就是自己产品的功能和成本, 即功能更多更丰富、成本更低。这里的成本指的是能为客户,也就是消息队列使用者,节省多少资源、人力成本。

因为对我们使用者来说,业务和大数据都不陌生,日常工作都需要进行业务和数据系统的开发。但是麻烦的问题是:在业务消息中,我们经常需要使用RocketMQ或RabbitMQ,在大数据系统中,我们又需要使用Kafka或Pulsar。

所以, 运维侧需要运维多款 MQ,研发侧需要学习使用多款MQ,这在一定程度上拉升了研发和运维的成本。如果有一款消息队列满足所有场景,只需要部署一款消息队列,就能满足所有业务的需求,这种设计思想是非常有商业价值的。

现在我们也可以看到业界主流的4款消息队列,在消息和流的融合上各有动作。

  • RabbitMQ因为开发语言、架构和社区的活跃度、定位的原因,基本不会走这条路。
  • Kafka虽然也强调云原生,但目前主要工作在自身的架构优化上,比如去ZooKeeper,暂时在消息方向没有提出明确概念。但在我看来,未来Kafka应该会往这个方向转变,因为流的场景始终会有瓶颈,打通一个新方向在商业上肯定是有价值的。
  • RocketMQ在消息领域已经非常成熟,社区也希望打通流的场景,扩展使用范围,提升竞争力,抢占市场,也在往这个方向努力。
  • Pulsar是一个新兴架构,没有历史包袱,主打的就是云原生的消息和流的融合架构,希望满足更多场景,解决更多业务需求。

总结

现在你应该能解答我们开头提出的问题了,广义上讲,消息队列是有缓冲作用、具备类发布和订阅能力的存储引擎。而技术方案如何选择消息队列,来源于业务场景和数据量。如果只需要最基本的生产和消费功能,可以不用标准消息队列产品,大部分公司或业务的场景下,一款消息队列就能满足所有需求了。

技术的演进都是商业驱动的,消息队列的演进,无论是从需求发展路径上看是消息 -> 流 -> 消息和流融合,还是从架构发展角度的单机 -> 分布式 -> 云原生/Serverless,本质其实都是在思考如何降低成本和吸引客户。

为了降低成本,弹性是最基础的要求。所以消息队列在技术上,对计算弹性的需求提出了计算存储分离架构,对低存储成本的需求提出了分层存储的概念,对资源复用的需求提出了多租户的概念。

为了吸引客户,各个消息队列都在尽量提高自己的竞争力,围绕着功能、容灾、多架构、生态建设展开。

不过要注意,消息和流只是业界的趋势,不是我们作为使用者必然的非此即彼的选择。在开发者实际使用的时候,我也发现很多人会将Kafka当做一个业务消息总线在用,也有人使用RocketMQ传递大流量的日志,当做大数据架构中的管道在用。

思考题

你现在做的项目业务场景和数据量是什么样的,是否会考虑引入消息队列,你会引入哪款消息队列呢?

期待看到你的留言,也欢迎你把这节课分享给感兴趣的朋友。我们下节课再见!