由于网络限制,转载一份 Eran Stiller 大师的作品。
============================ 分割线 =============================
介绍
作为一名处理大量基于微服务的系统的软件架构师,我经常会遇到一个反复出现的问题,“我应该使用RabbitMQ还是Kafka?” 出于某种原因,许多开发人员认为这些技术是可以互换的。虽然在某些情况下确实如此,但这些平台之间存在各种潜在差异。
因此,不同的场景需要不同的解决方案,选择错误的解决方案可能会严重影响您设计、开发和维护软件解决方案的能力。
这篇文章的目标是首先介绍基本的异步消息传递模式。然后,继续介绍 RabbitMQ 和 Kafka 以及它们的内部结构。第 2 部分重点介绍了这些平台之间的关键差异、它们的各种优点和缺点,以及如何在两者之间进行选择。
异步消息传递模式
异步消息传递是一种消息传递方案,其中生产者的消息生产与消费者的处理分离。在处理消息传递系统时,我们通常确定两种主要的消息传递模式——消息队列和发布/订阅。
消息队列
在消息队列通信模式中,队列暂时将生产者与消费者分离。多个生产者可以向同一个队列发送消息;但是,当消费者处理消息时,它会被锁定或从队列中移除,并且不再可用。只有一个消费者使用特定的消息。
消息队列
附带说明一下,如果消费者未能处理某个消息,消息传递平台通常会将消息返回到队列,在那里它可供其他消费者使用。除了时间解耦,队列允许我们独立扩展生产者和消费者,并提供一定程度的容错处理错误。
发布/订阅
在发布/订阅(或发布/订阅)通信模式中,单个消息可以由多个订阅者同时接收和处理。
发布/订阅
例如,此模式允许发布者通知所有订阅者系统中发生了某些事情。许多排队平台经常将发布/订阅与术语主题相关联。在 RabbitMQ 中,主题是一种特定类型的发布/订阅实现(准确地说是一种交换),但在本文中,我将主题称为发布/订阅整体的表示。
一般来说,订阅有两种类型:
- 一个临时订阅,其中订阅仅在消费者启动并运行时才处于活动状态。一旦消费者关闭,他们的订阅和尚未处理的消息就会丢失。
- 持久订阅,只要未明确删除订阅,就会保持订阅。当消费者关闭时,消息平台维护订阅,稍后可以恢复消息处理。
Rabbit MQ
RabbitMQ 是消息代理的实现——通常称为服务总线。它本身支持上述两种消息传递模式。其他流行的消息代理实现包括ActiveMQ、ZeroMQ、Azure Service Bus和Amazon Simple Queue Service (SQS)。所有这些实现都有很多共同点;本文中描述的许多概念适用于其中的大多数。
队列
RabbitMQ 支持开箱即用的经典消息队列。开发人员定义命名队列,然后发布者可以向该命名队列发送消息。消费者反过来使用相同的队列来检索消息以处理它们。
消息交流
RabbitMQ 通过使用消息交换来实现发布/订阅。发布者在不知道这些消息的订阅者是谁的情况下将其消息发布到消息交换。
每个希望订阅交换的消费者都会创建一个队列;然后消息交换将生成的消息排队以供消费者消费。它还可以根据各种路由规则为某些订阅者过滤消息。
RabbitMQ 消息交换
需要注意的是 RabbitMQ 支持临时订阅和持久订阅。消费者可以通过 RabbitMQ 的 API 决定他们想要使用的订阅类型。
由于 RabbitMQ 的架构,我们还可以创建一种混合方法——其中一些订阅者形成消费者组,这些消费者组以特定队列上的竞争消费者的形式共同处理消息。通过这种方式,我们实现了发布/订阅模式,同时还允许一些订阅者扩展以处理接收到的消息。
发布/订阅和排队相结合
Kafka
Apache Kafka 不是 消息代理的实现。相反,它是一个分布式流媒体平台。
与基于队列和交换的 RabbitMQ 不同,Kafka 的存储层是使用分区事务日志实现的。Kafka 还提供了 Streams API 来实时处理流,并提供 Connectors API 以方便与各种数据源集成;但是,这些超出了本文的范围。
云厂商为Kafka的存储层提供了替代解决方案。这些解决方案包括Azure 事件中心,在某种程度上,还包括AWS Kinesis Data Streams。Kafka 的流处理功能也有特定于云的开源替代方案,但同样,这些超出了本文的范围。
话题
Kafka 没有实现队列的概念。相反,Kafka 将记录集合存储在称为主题的类别中。
对于每个主题,Kafka 维护一个分区的消息日志。每个分区都是一个有序的、不可变的记录序列,其中不断附加消息。
Kafka 在消息到达时将消息附加到这些分区。默认情况下,它使用循环分区器在分区之间均匀地传播消息。
生产者可以修改此行为以创建逻辑消息流。例如,在多租户应用程序中,我们可能希望根据每个消息的租户 ID 创建逻辑消息流。在 IoT 场景中,我们可能希望每个生产者的身份不断映射到特定分区。确保来自同一个逻辑流的所有消息映射到同一个分区,以保证它们按顺序传递给消费者。
kafka 生产者
消费者通过维护这些分区的偏移量(或索引)并按顺序读取它们来消费消息。
一个消费者可以消费多个主题,消费者可以扩展到可用分区的数量。
因此,在创建主题时,应仔细考虑该主题的消息传递的预期吞吐量。一组共同消费一个主题的消费者称为消费者组。Kafka 的 API 通常处理消费者组中消费者之间的分区处理的平衡以及消费者当前分区偏移量的存储。
卡夫卡消费者
使用 Kafka 实现消息传递模式
Kafka 的实现很好地映射到发布/订阅模式。
一个生产者可以向一个特定的主题发送消息,多个消费者组可以消费同一条消息。每个消费者组都可以单独扩展以处理负载。由于消费者维护他们的分区偏移量,他们可以选择有一个持久订阅,在重新启动时保持其偏移量,或者一个临时订阅,每次启动时都会丢弃偏移量并从每个分区中的最新记录重新启动。
但是,它不太适合消息队列模式。当然,我们可以有一个只有一个消费者组的主题来模拟经典的消息队列。然而,这有多个缺点,本文的第 2部分详细讨论了这一点。
重要的是要注意,无论消费者是否消费这些消息,Kafka 都会在分区中保留消息长达预配置的时间。这种保留意味着消费者可以自由地重读过去的消息。此外,开发人员还可以使用 Kafka 的存储层来实现事件溯源和审计日志等机制。
结束语
虽然 RabbitMQ 和 Kafka 有时可以互换,但它们的实现却大不相同。因此,我们不能将它们视为同一类别工具的成员;一个是消息代理,另一个是分布式流媒体平台。
作为解决方案架构师,我们应该承认这些差异并积极考虑我们应该为给定场景使用哪些类型的解决方案。第 2 部分解决了这些差异并提供了有关何时使用每个差异的指南。