参考连接:超详细的RabbitMQ入门,看这篇就够了!-阿里云开发者社区 (aliyun.com),消息队列之 RabbitMQ - 简书 (jianshu.com),Kafka【入门】就这一篇! - 知乎 (zhihu.com),Kafka简明教程 - 知乎 (zhihu.com),Kafka高性能原理 - 知乎 (zhihu.com),Kafka高性能原理 - 知乎 (zhihu.com),RabbitMQ与Kafka选型对比 - 陈珙 - 博客园 (cnblogs.com)
1.RabbitMQ
1.1 RabbitMQ的组成部分
- Broker:消息队列服务进程。此进程包括两个部分:Exchange和Queue。
- Exchange:消息队列交换机。按一定的规则将消息路由转发到某个队列。
- Queue:消息队列,存储消息的队列。
- Producer:消息生产者。生产方客户端将消息同交换机路由发送到队列中。
- Consumer:消息消费者。消费队列中存储的消息。
使用Diect Exchange工作流程大致如下:
- 消息生产者连接到RabbitMQ Broker,创建connection,开启channel
- 生产者声明交换机类型、名称、是否持久化等。
- 生产者发送消息,并指定消息是否持久化等属性和routing key。
- exchange收到消息之后,根据routing key路由到跟当前交换机绑定的相匹配的队列(Diect Exchange情况)里面。
- 消费者监听接收到消息之后开始业务处理。
1.2 Exchange的四种类型
Direct Exchange
直连交换机意思是此交换机需要绑定一个队列,生产者发送消息给Exchange会指定一个Routing Key,要求该消息与一个特定的路由键完全匹配。简单点说就是一对一的,点对点的发送。它是完全匹配、单播的模式。
Fanout exchange
这种类型的交换机需要将队列绑定到交换机上。一个发送到交换机的消息都会被转发到与该交换机绑定的所有队列上。很像子网广播,每台子网内的主机都获得了一份复制的消息。
Topic exchange
topic 交换器通过模式匹配分配消息的路由键属性,将路由键和某个模式进行匹配,此时队列需要绑定到一个模式上。它将路由键和绑定键的字符串切分成单词,这些单词之间用点隔开。它同样也会识别两个通配符:符号#
和符号*
。#
匹配0个或多个单词,*
匹配一个单词。如下图所示:
Headers Exchange
这种交换机用的相对没这么多。它跟上面三种有点区别,它的路由不是用routingKey进行路由匹配,而是在匹配请求头中所带的键值进行路由。如图所示:
全部匹配情况:需要所有键都完全匹配
部分匹配情况:只需要部分匹配即可
2.Kafka
2.1 生产者与消费者
对于 Kafka 来说客户端有两种基本类型:生产者(Producer)和消费者(Consumer),生产者(也称为发布者)创建消息,而消费者(也称为订阅者)负责消费or读取消息。
2.2 主题(Topic)与分区(Partition)
在 Kafka 中,消息以**主题(Topic)**来分类,每一个主题都对应一个「消息队列」,这有点儿类似于数据库中的表。但是如果我们把所有同类的消息都塞入到一个“中心”队列中,势必缺少可伸缩性,无论是生产者/消费者数目的增加,还是消息数量的增加,都可能耗尽系统的性能或存储。
我们使用一个生活中的例子来说明:现在 A 城市生产的某商品需要运输到 B 城市,走的是公路,那么单通道的高速公路不论是在「A 城市商品增多」还是「现在 C 城市也要往 B 城市运输东西」这样的情况下都会出现「吞吐量不足」的问题。所以我们现在引入分区(Partition)的概念,类似“允许多修几条道”的方式对我们的主题完成了水平扩展。
一个队列(分区)只有一种topic,但是一种topic的消息却可以根据自定义的key值,分散到多条队列中。
2.3 Broker 和集群(Cluster)
一个 Kafka 服务器也称为 Broker,它接受生产者发送的消息并存入磁盘;Broker 同时服务消费者拉取分区消息的请求,返回目前已经提交的消息。使用特定的机器硬件,一个 Broker 每秒可以处理成千上万的分区和百万量级的消息。(现在动不动就百万量级…我特地去查了一把,好像确实集群的情况下吞吐量挺高的…摁…)
若干个 Broker 组成一个集群(Cluster),其中集群内某个 Broker 会成为集群控制器(Cluster Controller),它负责管理集群,包括分配分区到 Broker、监控 Broker 故障等。在集群内,一个分区由一个 Broker 负责,每个partition不再只有一个,而是有一个leader(红色)和多个replica(蓝色),生产者根据消息的topic和key值,确定了消息要发往哪个partition之后(假设是p1),会找到partition对应的leader(也就是broker2里的p1),然后将消息发给leader,leader负责消息的写入,并与其余的replica进行同步。
一旦某一个partition的leader挂掉了,那么只需提拔一个replica出来,让它成为leader就ok了,系统依旧可以正常运行。
通过Broker集群的设计,我们不仅解决了系统高可用的问题,还进一步提升了系统的吞吐量,因为replica同样可以为消费者提供数据查找的功能。
3.两者的对比
3.1 实现方式
RabbitMQ
RabbitMQ使用消息交换器来实现发布/订阅模式。发布者可以把消息发布到消息交换器上而不用知道这些消息都有哪些订阅者。
每一个订阅了交换器的消费者都会创建一个队列;然后消息交换器会把生产的消息放入队列以供消费者消费。消息交换器也可以基于各种路由规则为一些订阅者过滤消息。
Kafka
不同于基于队列和交换器的RabbitMQ,Kafka的存储层是使用分区事务日志来实现的。
Kafka没有实现队列这种东西。相应的,Kafka按照类别存储记录集,并且把这种类别称为主题。当消息到达时,Kafka就会把他们追加到分区尾部。
消费者通过维护分区的偏移(或者说索引)来顺序的读出消息,然后消费消息。
3.2 消息顺序
RabbitMQ
对于发送到队列或者交换器上的消息,RabbitMQ不保证它们的顺序。尽管消费者按照顺序处理生产者发来的消息看上去很符合逻辑,但是这有很大误导性。
只要我们是单个消费者,那么接收到的消息就是有序的。然而,一旦有多个消费者从同一个队列中读取消息,那么消息的处理顺序就没法保证了。由于消费者读取消息之后可能会把消息放回(或者重传)到队列中(例如,处理失败的情况),这样就会导致消息的顺序无法保证。
如下就是使用RabbitMQ丢失消息顺序的例子:
Kafka
默认情况下,Kafka会使用循环分区器(round-robin partitioner)把消息放到相应的分区上。不过,生产者可以给每个消息设置分区键(key)来创建数据逻辑流(比如来自同一个设备的消息,或者属于同一租户的消息)。我们可以伸缩一个主题中的分区数量,这样可以让每个分区分担更少的消息,然后增加更多的消费者来处理额外的分区。从而可以保障相同主题分区的所有消息都能够按照顺序处理。
3.3 消息路由
RabbitMQ可以基于定义的订阅者路由规则路由消息给一个消息交换器上的订阅者。一个主题交换器可以通过一个叫做routing_key的特定头来路由消息。
或者,一个头部(headers)交换器可以基于任意的消息头来路由消息。这两种交换器都能够有效地让消费者设置他们感兴趣的消息类型,因此可以给解决方案架构师提供很好的灵活性。
另一方面,Kafka在处理消息之前是不允许消费者过滤一个主题中的消息。一个订阅的消费者在没有异常情况下会接受一个分区中的所有消息。
作为一个开发者,你可能使用Kafka流式作业(job),它会从主题中读取消息,然后过滤,最后再把过滤的消息推送到另一个消费者可以订阅的主题。但是,这需要更多的工作量和维护,并且还涉及到更多的移动操作。
在消息路由和过滤方面,RabbitMQ提供了更好的支持。
3.4 消息时序
RabbitMQ
在测定发送到一个队列的消息时间方面,RabbitMQ提供了多种能力:
- 消息存活时间(TTL):发送到RabbitMQ的每条消息都可以关联一个TTL属性。发布者可以直接设置TTL或者根据队列的策略来设置。系统可以根据设置的TTL来限制消息的有效期。如果消费者在预期时间内没有处理该消息,那么这条消息会自动的从队列上被移除
- 延迟/预定的消息:RabbitMQ可以通过插件的方式来支持延迟或者预定的消息。当这个插件在消息交换器上启用的时候,生产者可以发送消息到RabbitMQ上,然后这个生产者可以延迟RabbitMQ路由这个消息到消费者队列的时间。
Kafka
Kafka也没用为消息提供TTL的机制,不过我们可以在应用层实现。
3.5 消息留存
当消费者成功消费消息之后,RabbitMQ就会把对应的消息从存储中删除。这种行为没法修改。它几乎是所有消息代理设计的必备部分。
相反,Kafka会给每个主题配置超时时间,只要没有达到超时时间的消息都会保留下来。在消息留存方面,Kafka仅仅把它当做消息日志来看待,并不关心消费者的消费状态。
消费者可以不限次数的消费每条消息,并且他们可以操作分区偏移来“及时”往返的处理这些消息。Kafka会周期的检查分区中消息的留存时间,一旦消息超过设定保留的时长,就会被删除。
Kafka的性能不依赖于存储大小。所以,理论上,它存储消息几乎不会影响性能(只要你的节点有足够多的空间保存这些分区)。
Kafka设计之初就是保存消息的,但是RabbitMQ并不是。
所有二者的工作方式也不一样RabbitMQ采用push的方式将消息推送到消费者,而kafka采用pull的方式,需要消费者从分区中读取。
3.6 性能差别
Kafka的诞生的是处理高并发日志的,吞吐量比较高,每秒请求数达到数十万量级,RabbitMQ每秒请求数则为万级别,有测试报告指出Kafka是RabbitMQ的10倍以上性能。
kafka的关键技术点:
- 异步批量处理(增大I/O尺度)
- 磁盘顺序读写(仅追加文件末尾)
- 操作系统PageCache缓存数据(读写操作直接在page cache上完成)
- 零拷贝加速消费
3.6 应用场景
对比项 | RabbitMQ | Kafka |
---|---|---|
吞吐量 | 低 | 高 |
有序性 | 全局有序性 | 分区有序性 |
消息可靠性 | 多策略组合 | 消息持久化 |
流处理 | 不支持 | 支持 |
时效性 | 高 | 中 |
运维便捷度 | 高 | 中 |
系统依赖 | 无 | zookeeper |
Web监控 | 自带 | 第三方 |
优先级队列 | 支持 | 不支持 |
死信 | 支持 | 不支持 |
客户端支持 | 支持多种语言 | |
社区生态 | 好 | 同左 |
安全机制 | (TLS/SSL、SASL)身份认证和(读写)权限控制 | 同左 |
消息回溯 | 支持 | 不支持 |
RabbitMQ:有消息的确认机制,用于实时的,对可靠性要求较高的消息传递上。在金融场景中经常使用,具有较高的严谨性,数据丢失的可能性更小,同事具备更高的实时性。
kafka:无消息的确认机制,优势主要体现在吞吐量上,主要用于处理活跃的流式数据,大数据量的数据处理上。