赞
踩
不同的业务场景需要不同的解决方案,选错一个方案会严重影响你对软件的设计、开发、维护能力。
RabbitMQ和Kafka在底层实现方面是有许多差异【详见文章最后】,需要根据你的特殊使用场景进行选择。
异步消息模式是解耦消息的生产和处理的一种解决方案。消息系统有两种消息模式:消息队列模式、发布/订阅模式
消息队列用于解耦生产者和消费者。多个生产者可以向同一个消息队列发送消息;但是一个消息在被一个消息者处理的时候,这个消息在队列上会被锁住或者被移除并且其他消费者无法处理该消息,即消息队列模式下一个具体的消息只能由一个消费者消费。
需要额外注意的是,如果消费者处理一个消息失败了,消息系统一般会把这个消息放回队列,这样其他消费者可以继续处理。消息队列除了提供解耦功能之外,它还能够对生产者和消费者进行独立的伸缩(scale),以及提供对错误处理的容错能力。
发布/订阅(pub/sub)模式中,单个消息可以被多个订阅者并发的获取和处理。
一个系统中产生的事件可以通过这种模式让发布者通知所有订阅者
Kafka使用topic表示发布/订阅,但RabbitMQ使用交换器(exchange)
一般来说,订阅有两种类型:
RabbitMQ作为消息中间件的一种实现,常常被当作一种服务总线来使用。RabbitMQ原生就支持上面提到的两种消息模式。【同时实现这两种消息模式的还有ActiveMQ,ZeroMQ】
RabbitMQ支持典型的开箱即用的消息队列。开发者可以定义一个命名队列,然后发布者可以向这个命名队列中发送消息。最后消费者可以通过这个命名队列获取待处理的消息。
RabbitMQ使用消息交换器来实现发布/订阅模式。发布者可以把消息发布到消息交换器上而不用知道这些消息都有哪些订阅者。
每一个订阅了交换器的消费者都会创建一个队列;然后消息交换器会把生产的消息放入队列以供消费者消费。消息交换器也可以基于各种路由规则为一些订阅者过滤消息。
RabbitMQ支持临时和持久两种订阅类型。消费者可以调用RabbitMQ的API来选择他们想要的订阅类型。
根据RabbitMQ的架构设计,可以创建一种混合方法——订阅者以组队的方式然后在组内以竞争关系作为消费者去处理某个具体队列上的消息,这种由订阅者构成的组我们称为消费者组。按照这种方式,实现了发布/订阅模式,同时也能够很好的伸缩(scale-up)订阅者去处理收到的消息。
kafka的官网介绍是:Apache Kafka是一个开源的分布式事件流平台,被数千家公司用于高性能数据管道、流分析、数据集成和关键任务应用。Kafka提供流式API用于实时的流处理、连接器API用来更容易的和各种数据源集成。
kafka不是消息中间件的一种实现,它只是一种分布式事件流系统
不同于基于队列和交换器的RabbitMQ,Kafka的存储层是使用分区事务日志来实现的。
Kafka使用topic表示一类消息,并按topic、分区进行消息存储。
Kafka为每个主题维护一个消息分区日志。每个分区都是由有序的不可变的消息组成,并且消息都是连续的被追加在尾部。
当消息到达时,Kafka就会把他们追加到分区尾部。默认情况下,Kafka使用轮询分区器(partitioner)把消息一致的分配到多个分区上。
Kafka可以改变创建消息逻辑流的行为,可以确保不同租户或设备产生的消息发送到相同的分区上,从而保证了消息能够按照顺序提供给消费者。
消费者通过维护分区的偏移(或者说索引)来顺序的读出消息,然后消费消息。
单个消费者可以消费多个不同的主题,并且消费者的数量可以伸缩到可获取的最大分区数量。
所以在创建主题的时候,需要认真的考虑一下t预期的消息吞吐量。
消费同一个主题的多个消费者构成的组称为消费者组。
通过Kafka提供的API可以处理同一消费者组中多个消费者之间的分区平衡以及消费者当前分区偏移的存储。
Kafka的实现很好地契合发布/订阅模式。
生产者可以向一个具体的主题发送消息,然后多个消费者组可以消费相同的消息。每一个消费者组都可以独立的伸缩去处理相应的负载。
由于消费者维护自己的分区偏移,所以可以选择持久订阅或者临时订阅
注意:
Kafka是按照预先配置好的时间保留分区中的消息,而不是根据消费者是否消费了这些消息。这种保留机制可以让消费者自由的重读之前的消息。另外,开发者也可以利用Kafka的存储层来实现诸如事件溯源和日志审计功能。
RabbitMQ是一个消息代理,而Kafka是一个分布式事件流平台,在内部实现方面有显著差异
对于RabbitMQ来说,由于消费者读取消息之后可能会把消息放回(或者重传)到队列中(例如,处理失败的情况),这样就会导致消息的顺序无法保证。一旦一个消息被重新放回队列,另一个消费者可以继续处理它,即使这个消费者已经处理到了放回消息之后的消息。因此,消费者组处理消息是无序的,如下表所示:
虽然可以通过限制消费者的并发数等于1来保证RabbitMQ中的消息有序性,但这么严重影响消息处理能力
对于Kafka来说,它在消息处理方面提供了可靠的顺序保证。Kafka能够保证发送到相同主题分区的所有消息都能够按照顺序处理。
默认情况下,Kafka会使用循环分区器(round-robin partitioner)把消息放到相应的分区上。不过,生产者可以给每个消息设置分区键(key)来创建数据逻辑流(比如来自同一个设备的消息,或者属于同一租户的消息)。所有来自相同流的消息都会被放到相同的分区中,这样消费者组就可以按照顺序处理它们。
每个分区都是由一个消费者的一个线程来处理,所以没法伸缩(scale)单个分区的处理能力。
Kafka可以保证单个分区的消息按顺序消费,RabbitMQ在这块就相对比较弱。
RabbitMQ可以基于定义路由规则 路由消息 给一个消息交换器上的订阅者:
Kafka在处理消息之前是不允许消费者过滤一个主题中的消息。一个订阅的消费者在没有异常情况下会接受一个分区中的所有消息。
在消息路由和过滤方面,RabbitMQ提供了更好的支持。
RabbitMQ可以设置消息存活时间(TTL),并提供延迟/预定消息的处理,kafka没有这些能力。
RabbitMQ在消息时序方面,提供了如下2个功能:
消息存活时间(TTL)
发送到RabbitMQ的每条消息都可以关联一个TTL属性。发布者可以直接设置TTL或者根据队列的策略来设置。
系统可以根据设置的TTL来限制消息的有效期。如果消费者在预期时间内没有处理该消息,那么这条消息会自动的从队列上被移除(并且会被移到死信交换器上,同时在这之后的消息都会这样处理)。
TTL对于那些有时效性的命令特别有用,因为一段时间内没有处理的话,这些命令就没有什么意义了。
延迟/预定的消息
RabbitMQ可以通过插件的方式来支持延迟或者预定的消息。当这个插件在消息交换器上启用的时候,生产者可以发送消息到RabbitMQ上,然后这个生产者可以延迟RabbitMQ路由这个消息到消费者队列的时间。
这个功能允许开发者调度将来(future)的命令,也就是在那之前不应该被处理的命令。例如,当生产者遇到限流规则时可能会把这些特定的命令延迟到之后的一个时间执行。
Kafka没有提供这些功能。它在消息到达的时候就把它们写入分区中,这样消费者就可以立即获取到消息去处理。
Kafka也没用为消息提供TTL的机制,不过我们可以在应用层实现。
必须要记住的一点是Kafka分区是一种追加模式的事务日志。所以,它是不能处理消息时间(或者分区中的位置)。
当消费者成功消费消息之后,RabbitMQ就会把对应的消息从存储中删除。这种行为没法修改。它几乎是所有消息代理设计的必备部分。
相反,Kafka会给每个主题配置超时时间,只要没有达到超时时间的消息都会保留下来。在消息留存方面,Kafka仅仅把它当做消息日志来看待,并不关心消费者的消费状态。
Kafka的性能不依赖于存储大小。所以,理论上,它存储消息几乎不会影响性能(只要你的节点有足够多的空间保存这些分区)。
Kafka设计之初就是保存消息的,但是RabbitMQ并不是
消息处理存在两种可能的故障:
RabbitMQ提供了诸如交付重试和死信交换器(DLX dead-letter-exchange)来处理消息处理故障。
DLX的主要思路是根据合适的配置信息自动的把路由失败的消息发送到DLX,并且在交换器上根据规则来进一步的处理,比如异常重试,重试计数以及发送到“人为干预”的队列。
当某个消费者在重试处理某条消息时,作为一个整体的消息处理逻辑不会被阻塞。所以,一个消费者可以同步地去重试处理一条消息,不管花费多长时间都不会影响整个系统的运行。
和RabbitMQ相反,Kafka没有提供这种开箱即用的机制。在Kafka中,需要开发者在应用层提供和实现消息重试机制。
由于消费者不能改变消息的顺序,所以我们不能够拒绝和重试一个特定的消息以及提交一个在这个消息之后的消息。你只要记住,分区仅仅是一个追加模式的日志。
一个应用层解决方案可以把失败的消息提交到一个“重试主题”,并且从那个主题中处理重试;但是这样的话就会丢失消息的顺序。
伸缩有两种方式:
有多个基准测试,用于检查RabbitMQ和Kafka的性能,尽管通用的基准测试对一些特定的情况会有限制,但是Kafka通常被认为比RabbitMQ有更优越的性能。
Kafka使用顺序磁盘I / O来提高性能。从Kafka使用分区的架构上看,它在横向扩展上会优于RabbitMQ,当然RabbitMQ在纵向扩展上会有更多的优势。
Kafka的大规模部署通常每秒可以处理数十万条消息,甚至每秒百万级别的消息,集群可以达到几十个节点。
典型的RabbitMQ部署包含3到7个节点的集群,并且这些集群也不需要把负载分散到不同的队列上,通常可以预期每秒处理几万条消息。
Kafka在伸缩方面更优并且能够获得比RabbitMQ更高的吞吐量
RabbitMQ使用的是智能代理和傻瓜式消费者模式。消费者注册到消费者队列,然后RabbitMQ把传进来的消息推送给消费者。RabbitMQ也有拉取(pull)API;不过,一般很少被使用。
RabbitMQ管理消息的分发以及队列上消息的移除(也可能转移到DLX)。消费者不需要考虑这块。
根据RabbitMQ结构的设计,当负载增加的时候,一个队列上的消费者组可以有效的从仅仅一个消费者扩展到多个消费者,并且不需要对系统做任何的改变。
Kafka使用的是傻瓜式代理和智能消费者模式。消费者组中的消费者需要协调他们之间的主题分区租约(以便一个具体的分区只由消费者组中一个消费者监听)。
消费者也需要去管理和存储分区偏移索引。幸运的是Kafka SDK已经为我们封装了,所以不需要开发者自己管理。
大部分情况下这两个消息平台都可以满足业务的要求。但具体选择哪一个,取决于你的特定需求。
如在一个事件驱动的架构系统中,可以使用RabbitMQ在服务之间发送命令,并且使用Kafka实现业务事件通知。
原因是事件通知常常用于事件溯源,批量操作,或者审计目的,因此Kafka的消息留存能力就显得很有价值。
相反,命令一般需要在消费者端做额外处理,并且处理可以失败,所以需要高级的容错处理能力,使用RabbitMQ会更好一点。
Kafka | RabbitMQ | |
---|---|---|
吞吐量 | 吞吐量大,随集群规模线性递增 | 相对来讲,吞吐量小 |
消息顺序 | 单个分区上严格的消息顺序 | 因为消费可能会重新放回队列,无法保证消息的顺序 |
消息路由 | / | 高级灵活的路由规则 |
消息时序 | / | 消息时序控制(控制消息过期【TTL】或者消息延迟) |
容错处理 | 需要开发者在应用层提供和实现消息重试机制 | 提供交付重试和死信交换器机制,消费者更有可能处理消息不成功的情景中 |
伸缩性 | 横向扩展,提高吞吐量 | 纵向扩展,提高处理性能 |
消息留存 | 延长消息留存时间,包括过去消息重放的可能 | 消费后即删除 |
消费者复杂度 | 复杂,需要管理和存储分区偏移索引 | 更简单的消费者实现 |
https://www.rabbitmq.com/getstarted.html
http://kafka.apache.org/
https://zhuanlan.zhihu.com/p/161224418
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。