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