RabbitMQ vs Kafka:正面交锋(2)

简介: RabbitMQ 是一个消息代理中间件,而 Apache Kafka 是一个分布式流处理平台。这种差异可能看起来只是语义上的,但它会带来严重的影响,影响我们方便地实现各种系统功能。例如 Kafka 最适合处理流数据,在同一主题同一分区内保证消息顺序,而 RabbitMQ 对流中消息的顺序只提供基本的保证。

第二部分:RabbitMQ 和 Kafka 的显著区别

RabbitMQ 是一个消息代理中间件,而 Apache Kafka 是一个分布式流处理平台。这种差异可能看起来只是语义上的,但它会带来严重的影响,影响我们方便地实现各种系统功能。

例如 Kafka 最适合处理流数据,在同一主题同一分区内保证消息顺序,而 RabbitMQ 对流中消息的顺序只提供基本的保证。

不过 RabbitMQ 内置了对重试逻辑和死信交换的支持,而 Kafka 将此类逻辑实现留给了用户。

消息顺序

image.png

RabbitMQ 对发送到队列或交换器的消息的顺序性提供了很少的保证。虽然消费者按照生产者发送消息的顺序处理消息似乎很合理,但其实并不是这样。

RabbitMQ 文档声明了以下有关其消息顺序的内容:

“在一个通道中发布的消息,经过一个交换机、一个队列和一个传出通道后,将按照发送的顺序被接收。” — RabbitMQ Broker Semantics

换句话说,当我们只有一个消息消费者,它就会按顺序接收消息。然而一旦我们有多个消费者从同一个队列读取消息,我们就无法保证消息的处理顺序。

发生这种缺乏排序保证的情况是因为消费者可能会在读取消息后将消息返回(或重新传递)到队列(例如在处理失败的情况下)。

一旦消息返回,另一个消费者就可以拿起它进行处理,即使它已经消费了后面的消息。因此多个消费者之间无法有序处理消息,如下图所示。

image.png

我们可以通过将消费者并发数限制为 1 来重新保证 RabbitMQ 中的消息顺序。更准确地说,单个消费者内的线程计数要限制为 1,因为任何并行的消息处理都可能导致消息乱序问题。

如果我们将自己限制为一个单线程消费者虽然能保证消息顺序,但这会严重影响我们系统扩展消息的处理能力,因此我们不应该轻易的这样做。

另一方面,Kafka 为消费者在消息处理时提供了可靠的排序保证。 Kafka 保证发送到同一主题分区的所有消息都按顺序处理。

如果你还记得第 1 部分,默认情况下,Kafka 使用循环分区程序将消息放置在分区中。但是生产者可以在每个消息上设置分区键,以创建逻辑数据流(例如来自同一设备的消息,或属于同一租户的消息)。

来自同一数据流的所有消息都会被放置在同一分区中,从而使消费者组按顺序处理它们。

我们应该注意到,在消费者组中,每个分区都是由单个消费者的单个线程处理的。因此我们无法扩展单个分区的处理。

不过在 Kafka 中,我们可以扩展主题内的分区数量,从而使每个分区接收更少的消息,并为额外的分区添加额外的消费者。

赢家

Kafka 是明显的赢家,因为它允许消息按顺序处理。 RabbitMQ 在这方面只有较弱的保证。

消息路由

image.png

RabbitMQ 可以根据订阅者定义的路由规则将消息路由到消息交换机的订阅者。

主题交换(topic exchange)可以基于名为 routing_key 的专用标头来路由消息。

标头交换(headers exchange)可以基于任意消息标头路由消息。这两种交换都有效地允许消费者指定他们有兴趣接收的消息类型,从而为架构师选择消息平台提供了极大的灵活性。

exchange-headers 官网解释:www.rabbitmq.com/tutorials/a…

topic exchange 官网解释:www.rabbitmq.com/tutorials/a…

Kafka 不允许消费者在轮询主题之前过滤主题中的消息。订阅的消费者无一例外地接收分区中的所有消息。

作为开发人员,你可以使用 Kafka 用于流作业,该作业从主题读取消息,过滤它们,然后将它们推送到消费者订阅的另一个主题。尽管也可以实现,但相比与 RabbitMQ 需要更多的努力和维护,并且需要更多的活动部件。

赢家

RabbitMQ 在路由和过滤消息供消费者使用时提供卓越的支持。

消息计时

image.png

RabbitMQ 提供了有关延时消息发送到队列的各种功能:

消息生存时间 (TTL)

TTL 属性可以与发送到 RabbitMQ 的每条消息相关联。设置 TTL 可以由发布者直接完成,也可以作为队列本身的策略来完成。

指定 TTL 允许系统限制消息的有效期。如果消费者没有及时处理它,那么它会自动从队列中删除(并转移到死信交换,稍后会详细介绍)。

TTL 对于时间敏感但经过一段时间而没有处理后就变得无关紧要的命令特别有用。

延迟/定时消息

RabbitMQ 通过使用插件支持延迟/预定消息。当在消息交换上启用此插件时,生产者可以向 RabbitMQ 发送消息,并且生产者可以延迟 RabbitMQ 将此消息路由到消费者队列的时间。

此功能允许开发人员安排未来的命令,这些命令在此之前不应该被处理。例如当生产者遇到限制规则时,我们可能希望将特定命令的执行延迟到稍后的时间。

Kafka 不支持此类功能。当消息到达时,它将消息写入分区,消费者可以立即使用它们。

此外 Kafka 没有为消息提供 TTL 机制,尽管我们可以在应用程序级别实现一种机制。

我们还必须记住,Kafka 分区是一个仅追加的事务日志。因此它无法操纵消息时间(或分区内的位置)。

赢家

RabbitMQ 毫无疑问地赢得了这一项目的胜利。

消息保留

image.png

一旦消费者成功消费消息,RabbitMQ 就会从存储中删除消息。此行为几乎是所有消息代理平台的一种设计,无法修改。

相比之下,Kafka 根据设计将所有消息保留至每个主题配置的超时时间。在消息保留方面,Kafka 不关心消费者的消费状态,因为它充当消息日志。

消费者可以根据需要消费每条消息,并且可以通过操纵分区偏移量“及时”来回移动。Kafka 会定期检查主题中消息的年龄,并驱逐那些足够老的消息。

Kafka 的性能不依赖于存储大小。因此从理论上讲,人们几乎可以无限期地存储消息,而不会影响性能(只要你的节点足够大来存储这些分区)。

赢家

Kafka 设计上就旨在消息保留,而 RabbitMQ 则不然。这里不需要竞争,Kafka 被宣布为获胜者。

故障处理

image.png

在处理消息、队列和事件时,开发人员通常会认为消息处理总是成功。毕竟由于生产者将每条消息都放置在队列或主题中,即使消费者处理消息失败,它也可以简单地重试,直到成功为止。

虽然表面上确实如此,但我们应该对这个过程进行更多思考。我们应该承认,消息处理在某些情况下可能会失败。我们应该优雅地处理这些情况,即使部分情况下需要人为干预。

处理消息时可能出现两类错误:

  • 瞬时故障 — 由于临时问题(例如网络连接、CPU 负载或服务崩溃)而发生的故障。我们通常可以通过一遍又一遍地重试来缓解这种失败。
  • 持续性故障 — 由于无法通过额外重试解决的永久性问题而发生的故障。这些失败的常见原因是软件错误或无效的消息模式(即有害消息)。

作为架构师和开发人员,我们应该问自己:“消息处理失败时我们应该重试多少次?两次重试之间应该等待多长时间?我们如何区分暂时性故障和持续性故障?”

最重要的是:“当所有重试都失败或遇到持续失败时,我们该怎么办?”

虽然这些问题的答案是特定于领域的,但消息传递平台通常为我们提供解决工具。

RabbitMQ 提供了传递重试和死信交换 (DLX) 等工具来处理消息处理失败。

DLX 的主要思想是 RabbitMQ 可以根据适当的配置自动将失败的消息路由到 DLX,并在此交换中对消息应用进一步的处理规则,包括延迟重试、重试计数以及交付给“人工干预”队列。

下文提供了有关在 RabbitMQ 中处理重试的可能模式的更多见解。

engineering.nanit.com/rabbitmq-re…

这里要记住的最重要的事情是,在 RabbitMQ 中,当消费者忙于处理和重试特定消息时(甚至在将其返回到队列之前),其他消费者可以并发处理该消息之后的消息。

当特定消费者重试特定消息时,整个消息处理不会被卡住。因此消息使用者可以根据需要同步重试消息,而不会影响整个系统。

image.png

与 RabbitMQ 相反,Kafka 不提供任何开箱即用的此类工具。对于 Kafka 我们需要在应用程序中提供和实现消息重试机制。

另外我们应该注意,当消费者忙于同步重试特定消息时,无法处理来自同一分区的其他消息。

我们无法拒绝并重试特定消息并提交该消息之后的消息,因为消费者无法更改消息顺序。正如你所记得的,分区只是一个仅追加日志。

有一种类型的解决方案是应用程序可以将失败的消息提交到“重试主题”并从那里处理重试,不过这样我们就会失去了消息的顺序性。

Uber 工程部提供了解决此类问题的示例,可以在 Uber.com 上找到。如果消息处理的延迟不是问题,那么使用普通 Kafka 可能就足够了。

Uber.com 地址:eng.uber.com/reliable-re…

image.png

赢家

RabbitMQ 是该项目上的赢家,因为它提供了一种开箱即用的解决此问题的工具。

扩展性

有多个基准测试可以测试 RabbitMQ 和 Kafka 的性能。

虽然通用基准测试对特定情况的适用性有限,但 Kafka 通常被认为比 RabbitMQ 具有更好的性能。 Kafka 使用顺序磁盘 I/O 来提高性能。

它使用分区的架构意味着它的水平扩展(横向扩展)比 RabbitMQ 更好,而 RabbitMQ 的垂直扩展(纵向扩展)更好。

大型 Kafka 的集群部署通常每秒可以处理数十万条消息,甚至每秒处理数百万条消息。

过去的时间里,Pivotal 团队发布了 RabbitMQ 集群可以每秒处理 100 万条消息的文章如下。然而它是在 30 个节点的集群上实现的,负载以最佳方式分布在多个队列和交换器上。

RabbitMQ Hits One Million Messages Per Second on Google Compute Engine,地址:tanzu.vmware.com/content/blo…

典型的 RabbitMQ 部署包括三到七个节点集群,这些节点集群不一定能最佳地分配队列之间的负载。这些典型的集群通常只能每秒处理数万条消息的负载。

赢家

虽然这两个平台都可以处理大量负载,但 Kafka 通常比 RabbitMQ 具有更好的扩展性,并且可以实现更高的吞吐量,从而赢得了这一轮。

然而值得注意的是,大多数系统都不会达到每秒上万的消息处理量!因此除非你正在构建下一个拥有数百万用户的热门软件系统,否则你不需要太关心扩展性,因为这两个平台都可以很好地为你服务。

消费者复杂性

image.png

RabbitMQ 使用智能代理人(smart-broker)和愚蠢消费者(dumb-consumer)的方法。消费者注册到消费队列上,RabbitMQ 会在消息进入时向它们推送消息以进行处理。RabbitMQ 消费者还具有主动拉取的功能。不过它使用的比较少。

RabbitMQ 自动向消费者分发消息以及从队列(可能是 DLX)中删除消息。消费者无需担心这些。

RabbitMQ 的结构还意味着,当负载增加时,队列的消费者组可以有效地从一个消费者扩展到多个消费者,而无需对系统进行任何更改。

image.png

另一方面,Kafka 使用愚蠢代理人(dumb-broker)和聪明消费者(smart-consumer)的方法。消费者组中的消费者需要协调它们之间主题分区的约定(以便消费者组中只有一个消费者监听特定分区)。

消费者还需要管理和存储其分区的偏移索引。幸运的是 Kafka SDK 为我们处理了这些,所以我们不需要自己管理。

不过当我们的负载较低时,单个消费者需要并行处理和跟踪多个分区,这需要消费者端有更多的资源。

此外,随着系统负载的增加,我们只能将消费者组的消费者数量扩大到等于主题中分区数量的程度。不过我们可以通过增加分区数来增加消费者数。

当系统负载减少时,我们无法删除已经添加的分区,从而浪费了消费者资源。

image.png

赢家

RabbitMQ 在设计上就是为愚蠢消费者(dumb-consumers)而设计的。所以它成为了这一轮的胜利者。

何时使用哪个?

现在我们面临一个价值百万美元的问题:“我们什么时候应该使用 RabbitMQ,什么时候应该使用 Kafka?”

如果我们总结以上差异,我们将得出以下结论:

一般情况下,RabbitMQ 是更好的选择:

  1. 先进灵活的路由规则。
  2. 消息计时控制(控制消息过期或消息延迟)。
  3. 高级故障处理功能,以防消费者无法处理消息(暂时或永久)。
  4. 更简单的消费者实现。

当我们需要以下条件时,Kafka 是更好的选择:

  1. 严格的消息排序。
  2. 消息保留较长时间,包括重放过去消息的可能性。
  3. 当传统解决方案无法满足我们对扩展性的需求时,Kafka 能够达到较高的规模。

我们可以使用这两个平台来用于大多数软件系统。然而作为架构师,我们有责任选择最适合这个系统的工具。在做出这种选择时,我们应该考虑如上所述的功能差异和非功能限制。

这些非功能限制包括:

  1. 当前平台的现有开发人员掌握知识。
  2. 托管云解决方案的可用性(如果适用)。
  3. RabbitMQ 和 Kafka 的运营成本。
  4. 我们的目标技术栈中 SDK 的可用性。

在开发复杂的软件系统时,我们可能会倾向于只使用一个消息平台来实现所有必需的消息传递功能。然而根据我的经验,在同一个系统中,同时使用这两个消息平台会带来很多好处。

例如,在基于事件驱动架构的系统中,我们可以使用 RabbitMQ 在服务之间发送命令,并使用 Kafka 来实现业务事件通知。

原因是事件通知通常用于事件溯源、批量操作(ETL 样式)或审计目的,因此 Kafka 的消息保留功能非常有价值。

另一方面,命令通常需要在消费者端进行额外的处理,这些处理可能会失败并且需要高级的故障处理功能,这适用于 RabbitMQ。

最后

我开始写这两篇文章系列的初衷是,许多开发人员认为 RabbitMQ 和 Kafka 是可以互换的。我希望大家看到这两篇文章后,能更加深入了解这些消息平台的内部构造以及它们之间的技术差异。这些差异反过来又会影响这些消息平台所服务的系统。RabbitMQ 和 Kafka 都很棒,可以适用于多种系统。

最后作为架构师,我们有责任了解每个系统的需求,确定其优先级,并选择最合适的解决方案。

目录
相关文章
|
4月前
|
消息中间件 Java Kafka
消息传递新纪元:探索RabbitMQ、RocketMQ和Kafka的魅力所在
【8月更文挑战第29天】这段内容介绍了在分布式系统中起到异步通信与解耦作用的消息队列,并详细探讨了三种流行的消息队列产品:RabbitMQ、RocketMQ 和 Kafka。其中,RabbitMQ 是一个基于 AMQP 协议的开源消息队列系统,支持多种消息模型;RocketMQ 则是由阿里巴巴开源的具备高性能、高可用性和高可靠性的分布式消息队列,支持事务消息等多种特性;而 Kafka 作为一个由 LinkedIn 开源的分布式流处理平台,以高吞吐量和良好的可扩展性著称。此外,还提供了使用这三种消息队列发送和接收消息的代码示例。总之,这三种消息队列各有优势,适用于不同的业务场景。
75 3
|
1月前
|
消息中间件 大数据 Kafka
大厂面试高频:Kafka、RocketMQ、RabbitMQ 的优劣势比较
本文深入探讨了消息队列的核心概念、应用场景及Kafka、RocketMQ、RabbitMQ的优劣势比较,大厂面试高频,必知必会,建议收藏。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:Kafka、RocketMQ、RabbitMQ 的优劣势比较
|
1月前
|
消息中间件 存储 监控
ActiveMQ、RocketMQ、RabbitMQ、Kafka 的区别
【10月更文挑战第24天】ActiveMQ、RocketMQ、RabbitMQ 和 Kafka 都有各自的特点和优势,在不同的应用场景中发挥着重要作用。在选择消息队列时,需要根据具体的需求、性能要求、扩展性要求等因素进行综合考虑,选择最适合的消息队列技术。同时,随着技术的不断发展和演进,这些消息队列也在不断地更新和完善,以适应不断变化的应用需求。
103 1
|
2月前
|
消息中间件 存储 监控
说说如何解决RocketMq消息积压?为什么Kafka性能比RocketMq高?它们区别是什么?
【10月更文挑战第8天】在分布式系统中,消息队列扮演着至关重要的角色,它不仅能够解耦系统组件,还能提供异步处理、流量削峰和消息持久化等功能。在众多的消息队列产品中,RocketMQ和Kafka无疑是其中的佼佼者。本文将围绕如何解决RocketMQ消息积压、为什么Kafka性能比RocketMQ高以及它们之间的区别进行深入探讨。
104 1
|
4月前
|
消息中间件 存储 监控
RabbitMQ、Kafka对比(超详细),Kafka、RabbitMQ、RocketMQ的区别
RabbitMQ、Kafka对比(超详细),Kafka、RabbitMQ、RocketMQ的区别,设计目标、适用场景、吞吐量、消息存储和持久化、可靠性、集群负载均衡
RabbitMQ、Kafka对比(超详细),Kafka、RabbitMQ、RocketMQ的区别
|
4月前
|
消息中间件 存储 关系型数据库
Kafka 与 RabbitMQ 如何选择使用哪个?
Kafka 与 RabbitMQ 如何选择使用哪个?
55 1
|
4月前
|
消息中间件 Kafka Apache
kafka vs rocketmq: 不要只顾着吞吐量而忘了延迟这个指标
这篇文章讨论了Apache RocketMQ和Kafka的对比,强调RocketMQ在低延迟、消息重试与追踪、海量Topic、多租户等方面进行了优化,特别是在小包非批量和大量分区场景下的吞吐量超越Kafka,适合电商和金融领域等高并发、高可靠和高可用场景。
142 0
|
2月前
|
消息中间件 存储 运维
为什么说Kafka还不是完美的实时数据通道
【10月更文挑战第19天】Kafka 虽然作为数据通道被广泛应用,但在实时性、数据一致性、性能及管理方面存在局限。数据延迟受消息堆积和分区再平衡影响;数据一致性难以达到恰好一次;性能瓶颈在于网络和磁盘I/O;管理复杂性涉及集群配置与版本升级。
103 1
|
2月前
|
消息中间件 Java Kafka
Flink-04 Flink Java 3分钟上手 FlinkKafkaConsumer消费Kafka数据 进行计算SingleOutputStreamOperatorDataStreamSource
Flink-04 Flink Java 3分钟上手 FlinkKafkaConsumer消费Kafka数据 进行计算SingleOutputStreamOperatorDataStreamSource
55 1
|
4月前
|
消息中间件 Java Kafka
Kafka不重复消费的终极秘籍!解锁幂等性、偏移量、去重神器,让你的数据流稳如老狗,告别数据混乱时代!
【8月更文挑战第24天】Apache Kafka作为一款领先的分布式流处理平台,凭借其卓越的高吞吐量与低延迟特性,在大数据处理领域中占据重要地位。然而,在利用Kafka进行数据处理时,如何有效避免重复消费成为众多开发者关注的焦点。本文深入探讨了Kafka中可能出现重复消费的原因,并提出了四种实用的解决方案:利用消息偏移量手动控制消费进度;启用幂等性生产者确保消息不被重复发送;在消费者端实施去重机制;以及借助Kafka的事务支持实现精确的一次性处理。通过这些方法,开发者可根据不同的应用场景灵活选择最适合的策略,从而保障数据处理的准确性和一致性。
345 9
下一篇
DataWorks