作为一名经验丰富的微服务系统架构师,我经常被问到,我应该选择RabbitMQ还是Kafka?图片来自Pexels出于某些原因,很多开发者会将这两种技术视为等同的。确实,在某些case场景下选择RabbitMQ还是Kafka并没有什么区别,但是这两种技术在底层实现上有很多区别。不同的场景需要不同的解决方案,选择错误的解决方案会严重影响你设计、开发和维护软件的能力。本文将首先介绍基本的异步消息模式,然后介绍RabbitMQ和Kafka及其内部结构信息。第二部分(未完成)主要介绍这两种技术的主要区别和各自的优缺点。最后,我们将解释如何在这两种技术之间进行选择。异步消息模式异步消息可以作为消息生产和处理解耦的解决方案。谈到消息系统,我们通常会想到两种主要的消息模式——消息队列和发布/订阅模式。消息队列使用消息队列来解耦生产者和消费者。多个生产者可以向同一个消息队列发送消息。但是,当一条消息被消息发送者处理时,该消息将在队列中被锁定或删除,其他消费者将无法处理该消息。也就是说,一条特定的消息只能被一个消费者消费。应额外注意消息队列。如果某个消费者处理消息失败,消息系统一般会将消息放回队列中,以便其他消费者继续处理。除了提供解耦,消息队列还可以独立扩展生产者和消费者,并为错误处理提供容错能力。发布/订阅在发布/订阅(pub/sub)模式下,一条消息可以被多个订阅者并发获取和处理。发布/订阅例如,系统中生成的事件可以由发布者通过此模式传达给所有订阅者。许多队列系统中经常使用术语主题来指代发布/订阅模式。在RabbitMQ中,主题是发布/订阅模型的具体实现(更准确地说,是一种交换),但在本文中,我将主题和发布/订阅视为等价的看待。一般而言,有两种类型的订阅:临时订阅,仅在消费者启动并运行时存在。一旦消费者退出,相应的订阅和未处理的消息就会丢失。Durable(持久)订阅,这种订阅会一直存在,除非主动删除。消费者退出后,消息系统会继续维护订阅,后续的消息可以继续处理。RabbitMQRabbitMQ作为消息中间件的实现,常用作服务总线。RabbitMQ原生支持上述两种消息模式。其他一些流行的消息传递中间件实现是ActiveMQ、ZeroMQ、Azure服务总线和亚马逊简单队列服务(SQS)。这些消息传递中间件实现有很多共同点;本文中提到的大多数概念都适用于这些中间件。队列RabbitMQ开箱即用地支持典型的消息队列。开发者可以定义一个命名队列,然后发布者可以向这个命名队列发送消息。最后,消费者可以通过这个命名队列获取待处理的消息。消息交换RabbitMQ使用消息交换来实现发布/订阅模式。发布者可以在不知道这些消息有哪些订阅者的情况下将消息发布到消息交换器。每个订阅交换的消费者都会创建一个队列;然后消息交换将生产的消息放入队列中供消费者消费。消息交换还可以根据各种路由规则为某些订阅者过滤消息。RabbitMQ消息交换重要的是要注意RabbitMQ支持临时和持久订阅类型。消费者可以调用RabbitMQ的API来选择他们想要的订阅类型。根据RabbitMQ的架构设计,我们还可以创建一种混合的方式——订阅者组成一个团队,然后作为消费者在组内以竞争关系的方式处理特定队列上的消息。我们称之为消费者群体的群体。这样,我们实现了发布/订阅模型,同时我们可以扩容订阅者来处理接收到的消息。发布/订阅和队列的组合使用ApacheKafkaApacheKafka不是消息中间件的实现。相反,它只是一个分布式流媒体系统。与基于队列和交换的RabbitMQ不同,Kafka的存储层是使用分区事务日志实现的。Kafka还提供了用于实时流处理的StreamingAPI和用于更轻松地与各种数据源集成的连接器API;当然,这些都超出了本文的范围。云供应商为Kafka存储层提供了可选的解决方案,例如AzureEventHubsy和AWSKinesisDataStreams。还有针对Kafka流功能的云特定和开源解决方案,但同样,它们超出了本文的范围。TopicKafka没有实现队列这样的东西。相应地,Kafka将记录集按类别存储,并将此类类别称为主题。Kafka为每个主题维护一个分区的消息日志。每个分区由一个有序的不可变记录序列组成,并且消息被连续附加。当消息到达时,Kafka将它们附加到分区的末尾。默认情况下,Kafka使用循环分区程序在多个分区之间一致地分发消息。Kafka可以改变创建逻辑消息流的行为。例如,在多租户应用程序中,我们可以根据每个消息中的租户ID创建消息流。在物联网场景下,我们可以根据生产者的身份,将生产者映射到一个恒定级别的特定分区。确保来自同一逻辑流的消息映射到同一分区可确保消息按顺序传递给消费者。Kafka生产者和消费者通过维护分区偏移量(或索引)顺序读取消息,然后消费消息。单个消费者可以消费多个不同的主题,消费者的数量可以扩展到它可以获取的最大分区数。所以在创建主题时,我们必须认真考虑创建主题的预期消息吞吐量。消费相同主题的多个消费者组成的一组称为消费者组。Kafka提供的API可以处理同一个消费者组中多个消费者之间的分区平衡,以及消费者当前分区偏移量的存储。Kafka消费者Kafka对消息传递模式的实现Kafka的实现非常适合发布/订阅模式。生产者可以向特定主题发送消息,然后多个消费者组可以消费同一条消息。每个消费者组都可以独立扩展以处理相应的负载。由于消费者维护自己的分区偏移量,因此他们可以选择持久订阅或临时订阅。持久订阅在重启后不会丢失偏移量,而临时订阅在重启后会丢失偏移量,并在每次重启后从分区中更新。记录开始被读取。但是,这种实现方案并不能完全等同于典型的消息队列方式。当然,我们可以创建一个主题,关联一个有消费者的消费者组。这样,我们就模拟了一个典型的消息队列。然而,这有很多缺点,我们将在第二部分详细讨论。值得注意的是,Kafka在预先配置的时间内将消息保留在分区中,而不是消费者是否消费它们。这种保留机制允许消费者自由地重读以前的消息。此外,开发者还可以利用Kafka的存储层实现事件追踪、日志审计等功能。结论尽管有时可以认为RabbitMQ和Kafka是等同的,但它们的实现却大不相同。所以我们不能把它们当作同一种工具;一个是消息中间件,一个是分布式流媒体系统。作为解决方案架构师,我们需要能够识别它们之间的差异,并尝试考虑在给定场景中使用哪种类型的解决方案。第二部分(不完整)将指出这些差异,并提供何时使用哪种解决方案的指导,稍后会为大家更新。
