当前位置: 首页 > 科技观察

Kafka、RocketMQ、Pulsar综合对比!

时间:2023-03-11 22:53:43 科技观察

图片来自宝途网随着大数据时代的到来,Apache的Kafka一度成为消息队列的代名词。提到消息队列,大家自然会想到Kafka。但是,消息队列本身是工程领域问题的通用解决方案。其背后有一些共同的设计思想和经典模型,是消息队列的精髓和灵魂。它们独立于任何消息队列(如Kafka)的具体实现,但每一个消息队列(除了Kafka、RocketMQ、Pulsar等)的实现处处都体现着这些设计思想。本文主要从抽象层面简单说说消息队列背后的一些设计思想,辅助理解消息队列等组件。主要解决三个问题:消息队列适用于哪些场景?消息队列的主流产品有哪些以及各自的优缺点?消息队列背后的设计思路(整体核心模型、数据存储考虑、数据获取方案对比、消费者消费模型)messagequeue适用于哪些场景?消息队列:主要用于临时存储生产者产生的消息,供其他消费者后续消费。它主要有两个功能:暂存(storage)队列(有序:先进先出)其他大部分场景对数据消费没有顺序要求,主要是利用它的暂存能力。从目前使用消息队列的互联网应用来看,主要有三种场景:异步数据处理系统应用解耦业务流量调峰下面分别对上述各场景进行简单介绍。①异步数据处理第一个例子,我们用现实生活中的快递来类比。在这个例子中,我们将临时存放快递的快递柜比作临时存放数据的消息队列。让我们来看看现实生活。在没有快递柜的情况下,快递员通常需要在将快递送到目的地后联系收货人签收快递。如果此时收货人有空,一切都会很顺利。但如果此时收货人不方便(开会、吃午饭、出差……)。这对于快递员来说是非常尴尬的,他需要一直等待(开会或午餐)或者把快递员带回去(出差),造成时间的浪费。这对快递员来说简直太不友好了。从这里可以看出,快递员在送货的时候,是处于同步状态,即需要等待收货人签收后,才能发出下一个订单,这对快递员来说效率太低了。上面的例子虽然有点牵强,但是大家也能理解,能大概理解意思就可以了。那我们再来看看。当有快递柜时,对于快递员来说,每次需要派送快递时,只需要将快递扔进快递柜,然后通过短信或电话的方式将具体的快递信息通知收货人即可。能。他可以继续交付下一个订单。至于收割者,他们也可以根据具体方便的时间来收拾物品。这样,两者就完全异步了,不需要互相等待。在这个例子中,如果把快递员比作生产者,把收货人比作消费者,那么快递柜就类似于一个消息队列。我们可以使用消息队列来实现异步数据处理。②系统应用解耦案例2,我们以最主流的推荐系统中的内容流转为例。在推荐系统中,当创作者发布一条内容时,该内容会先通过安全部分的相关审核。审核完成后,通常需要将内容存入库,并发送给模型进行特征计算和生成。如果后期想要提升推荐效果,需要单独搭建一个冷启动推荐池。这部分内容也是这个时候需要的。那么问题就来了。当不使用消息队列时,对于上游服务,需要通过扩展新的逻辑来实现这个功能。同时,在这个场景下,会存在对三个下游服务的依赖。如果其中一个下游服务失败了,应该怎么办,是重试还是返回失败等细节。如果这部分数据后期要分发到其他渠道,怎么接。显然,这种场景面临着系统紧耦合的问题。我们再来看看。如果我们一开始就引入消息队列,会出现什么问题呢?内容审核通过后,直接生产数据丢入消息队列,多个下游服务从消息队列开始消费数据。当后续的数据需要扩展供其他系统使用时,只要将新的消费者连接到消息队列进行消费就可以了。不要对上游产生消息的模块进行任何更改。这样,我们就通过消息队列实现了系统应用的解耦。这是消息队列的第二次使用。③业务流量调峰报文对应的第三种使用场景是调峰。在当今的互联网世界,电商场景中每年的6.18闪购和双11抢购就是最典型的案例。这种场景下,系统的峰值流量往往集中在短时间内,正常流量相对可控。因此,为了防止系统在短时间内被高峰流量压垮,往往会使用消息队列来削弱高峰流量。高峰期产生的订单消息等数据,先送入消息队列暂存,下游系统根据自身消费能力逐步处理。同时,这类消息往往对延迟的要求不是很高,所以使用消息队列暂存比较合适。我们对本节的内容做一个简单的总结。以上通过三个简单的例子介绍了消息队列的三种典型使用场景:异步、解耦、调峰。从另一个角度可以看出,消息队列主要适用于对消息的处理要求不是很实时的场景,一条数据可能同时在多个地方使用,不同的用户有不同的处理费率。更多消息队列的使用场景,读者可以自行查找资料阅读总结。消息队列“家族”的成员有哪些?①消息队列的主流产品上图是按照时间线在不同时间点产生的消息队列产品。主要产品有:ActiveMQ(2003)RabbitMQ(2006)Kafka(2010)RocketMQ(2011)这些消息队列我们在Pulsar(2012)中或多或少都听说过,其中一些已经在项目中实际使用过。下面对上述消息队列进行简单介绍。ActiveMQ:ActiveMQ是Apache软件基金会基于Java语言开发的开源消息代理。能够支持多个客户端或服务器。计算机集群等属性支持ActiveMQ管理通信系统。RabbitMQ:RabbitMQ是一个开源消息代理软件(也称为面向消息的中间件),它实现了高级消息队列协议(AMQP)。RabbitMQ服务器是用Erlang语言编写的,而集群和故障转移是建立在OpenTelecomPlatform框架上的。所有主要的编程语言都有用于与代理接口通信的客户端库。RabbitMQ支持多种消息协议、传递确认和其他特性。Kafka:ApacheKafka是由ApacheSoftwareFoundation开发并使用Scala编写的开源消息系统项目。Kafka最初由LinkedIn开发,2011年初开源。2012年10月毕业于ApacheIncubator。该项目的目标是提供一个统一的、高吞吐量、低延迟的实时数据处理平台。Kafka是一种分布式、分区、多副本的日志提交服务。它通过独特的设计提供消息系统的功能。RocketMQ:ApacheRocketMQ是一个分布式消息和流媒体平台,具有低延迟、强一致性、高性能和可靠性、万亿级容量和灵活的扩展性。它借鉴了Kafka的设计思想,但不是Kafka的翻版。Pulsar:ApachePulsar是Apache软件基金会的顶级项目。是集消息、存储、轻量级函数计算于一体的下一代云原生分布式消息流平台,采用计算和存储分离的架构设计。支持多租户、持久化存储、多机房跨地域数据复制。具有强一致性、高吞吐量、低延迟、高扩展性等特点。它被视为云原生时代的实时消息流传输和存储。并计算最优解。②不同消息队列的比较上图详细展示了几种消息队列各自的功能和优缺点。首先,ActiveMQ和RabbitMQ属于同一个数量级,吞吐量比后三者差一个数量级。其次,RocketMQ和Pulsar支持强一致性,这些产品可以用在对消息一致性要求高的场景。同时,Kafka虽然存在数据丢失的风险,但是吞吐量比较高,社区非常活跃。大数据的大部分场景,都可以使用Kafka。最后,Kafka、RocketMQ、Pulsar都是基于磁盘存储数据,内存加速访问。ActiveMQ和RabbitMQ使用内存存储数据,也支持数据持久化到磁盘。消息队列背后的设计思想在前面。第一节主要介绍了为什么要使用消息队列,消息队列适合解决什么问题?在第二部分中,还介绍了有哪些可选的消息队列,以及它们各自的优缺点。这部分是最重要的内容。主要介绍了上述消息队列背后的一些通用设计思想。一些想法可以扩展到其他商业模式或领域。后面也会提到相应的内容。①消息队列核心模型上图是几乎所有消息队列设计的核心模型。对于一个消息队列,从数据流的维度,可以拆解为三个部分:生产者、消息队列集群、消费者。数据从生产者流向消息队列集群,最后从消息队列集群流向消费者。这些概念解释如下。生产者:生产数据的服务,通常也称为数据输入提供者。这里的数据通常是指我们的业务数据,比如推荐场景中的内容用户点击数据、内容曝光数据、电商中的订单数据等等。生产者通常作为客户端存在,但是在支持事务性消息的消息队列中,生产者也被设计为服务器来实现事务性消息的特性。其次,通常有多个生产者,消息队列集群内部有多个分区队列,所以生产者在发送数据时,通常会有一些负载均衡策略,比如keyhash、polling、random等方式。它的本质是一段数据,被消息队列封装后也称为消息。此消息只能发送到消息队列集群内部的分区队列。因此,只需按照一定的策略从多个队列中选择一个队列即可。消息队列集群:消息队列集群是消息队列等组件的实现核心。它的主要功能是存储消息、过滤消息和分发消息。其中,存储消息主要是指生产者生产的数据需要存储在消息队列中;存储消息可以说是消息队列的核心,一个消息队列的吞吐量和性能都离不开它的存储模型。这部分将在下一部分介绍。仅过滤消息是指消息队列可以通过一定的规则或策略来过滤消息。这种能力通常称为消息路由。过滤消息是一个高级特征函数。AMQP协议对这些能力进行了比较完整的抽象。有些消息队列可以选择性地实现协议来实现这个功能。读者可以搜索AMQP协议内容的相关资料阅读。这里就不展开了。.分发消息是指消息队列通常需要将消息分发给处理相同逻辑的多个消费者,或者分发给处理不同逻辑的不同消费者。分发消息可以说是和消费者模型挂钩的。这会涉及到不同的数据获取方式和消费者消费消息的模型。另外,大部分的消息队列还支持消息的分类。分类后的标签称为主题(topics),一个主题存储同一类型的消息。消费者:最终消息队列中存储的消息会被消费者消费,消费者也可以看成是消息队列中数据的输出方。消费者通常有两种方式从消息队列中获取数据:推(push)数据、拉(pull)数据。其次,消费者经常作为客户端出现在消息队列等组件中。②消息队列数据组织在这一节中,我们将仔细研究在消息队列中存储消息的过程中的一些权衡。通常,数据存储有两种:一种是存储在非易失性存储中,如磁盘介质。另一种选择是存储在易失性存储器中,通常是内存。两者的对比大家可以参考下表,这里就不赘述了。通常,在设计大多数组件时,往往会选择一种主要介质进行存储,另一种介质作为辅助使用。以Redis为例,它主要使用内存存储数据,磁盘辅助持久化。以RabbitMQ为例,它同样主要使用内存来存储消息,但也支持将消息持久化到磁盘。对于RocketMQ、Kafka、Pulsar,数据主要存储在磁盘上,内存用于帮助提升系统性能。MySQL等关系型数据库也主要使用磁盘来组织数据,合理使用内存来提高性能。对于使用内存存储数据的方案,难点在于如何在不降低访问效率的情况下,充分利用有限的内存空间存储尽可能多的数据。在这个过程中,数据结构的选择和优化是必不可少的。.另一方面,如何保证数据尽可能少的丢失,我们可以看到解决这个问题的方法通常是广义上的快照+wal文件。这种类型的典型代表是Redis。对于使用磁盘存储数据的方案,难点在于如何根据系统要解决的特点场景,合理安排磁盘布局。在读多写少的情况下,使用B+树来存储数据;对于写多读少的情况,采用lsmtree等解决方案进行处理。另一方面,如何尽可能减少对磁盘的频繁访问,一些方法使用mmap进行内存映射,以提高读取性能;其他人使用缓存机制来缓存经常访问的数据。还有一些则采用巧妙的数据结构布局,充分利用磁盘预读特性来保证系统性能。一般来说,对于写磁盘的优化,要么使用顺序写来提高性能,要么使用异步写磁盘来提高性能(异步写磁盘需要结合wal来保证数据的持久化,实际上wal也是主要使用顺序写入特性)。针对磁盘读取优化,一方面cache,另一方面mmap内存映射,加快读取速度。在Kafka、RocketMQ和Pulsar中可以看到上述存储解决方案中的权衡。其实抛开消息队列不谈,无论是关系型数据库还是kv类组件,这些存储方案的选择都是通用的。下图列出了磁盘上的几种数据组织方式,仅供大家参考:③获取数据的push和pull方案对比前面提到,消费者从消息队列中获取数据时,主要有两种方案:等待推送数据主动拉取数据当前消息队列实现时,会选择至少支持这两个选项中的一个。关于这两种方案的对比,可以参考下表。在这里,我个人想和消息队列无关,谈谈对这两种方案的理解。事实上,推拉模型不仅仅用在消息队列等组件中。从更一般的意义上讲,它实际上解决了数据传输双方之间的一个问题。问题。本质是数据需要从一方流向另一方。按照这个思路,下面三个例子都遵循这个原则。网络中传输的数据:在IO多路复用中,以epoll为例,当内核检测到被监控的描述符有数据到来时,epoll_wait()会从阻塞中返回,上层用户态程序就会知道有数据准备好了,然后可以通过read()系统调用函数读取数据。这个进程就绪通知类似于推送,但是推送的不是数据,而是数据准备好的信号。具体的数据获取方式还是需要拉取主动阅读。FeedsStreamingSystemUserTimeline后台实现方案(ReadDiffusion,WriteDiffusion):ReadDiffusion和WriteDiffusion就是这样的一个案例。对于readdiffusion,主要通过拉取的方式获取数据。对于writeflooding,就是一种典型的数据推送方式。当然,在系统实现中,更复杂的场景往往是通过读写结合来实现的。日常生活中的外卖点餐示例:外卖点餐通常有两种方式可供选择:外卖送餐和门店自取。但是通常外卖送货比较实时,我们一般都会选择这种方式。可见,外卖其实是push方式,而门店自提是pull方式。④消息队列消费模型本节介绍最后一部分,消息队列消费者的消费模型。下图上半部分展示了最简单的消费模型。一个生产者,一个消费者。但是往往我们的一条数据通常被不同的场景使用。这时候首先每个场景都需要用到全量的数据,不同场景之间不会有关联,相互独立。为了方便理解,我们假设有N个场景需要使用同一个数据,每个场景都需要消费全量数据。在N种场景中的一种情况下,多个消费者将共享并共同消费数据。我们假设一个场景中有M个消费者。由于每个场景包含M个消费者,我们也将其描述为一个消费者组。通过上面的介绍,我们可以用下面一句话来概括消息队列中的消费模型:consumer消费模型其实是1:N:M的关系,一条数据被N个消费组独立使用,每个消费者买家群中有M个消费者共享消费。其实这种模式也叫发布-订阅模式。对于消息,组间广播和组内单播。一条消息只能被一个消费者组中的一个消费者消费。消费组内部也有一些负载均衡策略。常用的有:轮询、随机、哈希、一致性哈希等方案。在这一部分,我们重点介绍了消息队列背后的一些设计思想,包括:消息队列的核心模型、数据存储模型、获取数据比较的推拉方案、consumer消费模型。其中,数据存储模型不仅适用于消息队列,也适用于其他数据存储组件的方案选择。同样,数据获取的推拉方案也不局限于消息队列。我们在很多业务场景中都能看到类似思维的影子。总结到这里,本文就告一段落了。这篇文章主要从理论和抽象层面讲了关于消息队列的一些想法和概念。主要介绍了消息队列的使用场景,主流的消息队列选项及其优缺点。最后介绍了消息队列背后的一些设计理念。本文只是一个介绍,希望以上内容可以帮助大家一起重新认识消息队列。后续我们会逐步选取上述消息队列(Kafka、RocketMQ、Pulsar),重点分析其内部实现机制,敬请期待。由于个人水平有限,如有理解上的错误,欢迎批评指正。参考资料:ActiveMQ和RabbitMQ的区别Kafka、ActiveMQ、RabbitMQ、RocketMQ的区别及高可用原理Kafka、RabbitMQ、RocketMQ等消息中间件的比较ApachePulsar在腾讯计费场景中的应用KafkaPushvs.pullMessagequeue-push/Pull模式学习&ActiveMQ和JMS学习作者:文晓飞简介:腾讯Cloud9项目组后台开发工程师。2年后台开发经验,熟悉推荐系统后台工作;对网络、存储、分布式共识算法(raft)等技术感兴趣。编辑:陶家龙来源:转载自公众号后台技术员(ID:wenxiaofeiCode)