Kafka 介绍
Kafka 是目前除 RabbitMQ 之外另一个使用广泛的消息队列(RocketMQ 在基本架构上借鉴了 Kafka)。Kafka 采用了和 RabbitMQ 完全不同的实现方式,相比 RabbitMQ, Kafka 提供了更高的吞吐量和完善的高可用机制。
Kafka 其实并不算是传统意义上的 MQ,它对 MQ 的支持是它功能的一个子集。相比于传统意义上的 MQ 系统,Kafka 更专注于高效地 传递和转换 消息,更像是一个消息引擎。
Kafka 的一些核心概念:
Topic 主题
主题用于分类消息,每个消息都有一个特定的主题,主题使用 name 进行区分。下游消费者订阅不同的主题,获取不同类型的消息。
Partition 分区
分区用于保存消息。每个主题至少包含一个分区,通常是包含多个分区,序号从 0 开始。分区可以跨服务器,因此分区能显著提高 Kafka 的吞吐量。主题在逻辑层面区分消息,分区则是在物理层面保存消息。分区底层还会细分为 Segment,分段用于优化分区的保存逻辑,防止分区保存的消息太大,影响读取和清理的性能。分段在客户端层面是透明的;
分区支持多副本。一个主分区可以配置多个从分区,从分区负责主动备份主分区的数据,实现故障转移。
Event Or Message 消息
消息是业务产生的数据。Kafka 的消息主要包含以下内容:
- Key:消息键。Kafka 用 key 来确定消息保存到哪个分区。如果 key 为 null,Kafka 就使用轮询的方式保存消息到不同的分区,否则就根据 Hash 算法计算分区位置。
- Value:消息体。业务数据,通常经过了压缩;
- Timestamp:时间戳。消息创建的时间,通常是客户端自动生成,也可以手动指定;
- Compression type:压缩类型。标记消息体的压缩算法,如
gzip
、lz4
、snappy
、zstd
或none
; - Headers:消息头,通常指定一些额外信息,可以为空;
- Topic & Partition number & Offset id:消息一旦写入 Kafka,就会自动带上这三个参数。这三个参数用来唯一定位一条消息;
Offset 偏移量
偏移量用来标记消息在分区中的位置。偏移量是一个从 0 开始递增的数字。
Broker 服务实例
Broker 是一台运行着 Kafka 服务的服务器。Kafka 集群包含多个 Broker,每个 Broker 包含一些主题和分区。
Consumer Group 消费者组
消费者 组是 Kafka 用于支持点对点模式引入的概念。一个消费者组下面包含多个 consumer,这些 consumer 都消费同一个主题下的消息,不同的 consumer 消费不同的 partition。每个 partition 只能被一个 consumer 消费,但一个 consumer 可以消费多个 partition,该机制确保 Topic 下的每条消息只能被 Consumer group 中的一个消费者消费。(但可以被多个消费者组消费)
如果主题下面的分区数大于消费者组下面的服务实例,那么有些消费者就会消费多个分区的消息。如果两者正好相等,那么每个消费者消费一个分区。如果分区数小于消费者数,那么有些消费者就无法消费,只有等到其他消费者宕机或网络故障导致消费失败后,其他消费者才能顶替原来的消费者进行消费。
Kafka 高性能的原因
Kafka 最大的特点就是其超高的吞吐量,RabbitMQ 的吞吐量是万级,Kafka 吞吐量在十万级。这主要归功于 Kafka 独特的设计,主要有以下几点:
磁盘顺序读写
磁盘的顺序读写性能比随机读写高出许多。有机构专门做过研究 :
Kafka 就是利用了磁盘顺序读写的特性,在 Kafka 底层,消息是保存在 partition 文件中的。为了避免大文件读写,Kafka 将 partition 文件细分为 segment 小文件,每次保存消息都是 追加 到对应文件末尾,而不是随机写入,这使得 Kafka 写入的吞吐量得到显著提高。
但这种方式有一个缺陷,就是不能删除数据。因此 Kafka 是不会立即删除消费成功的消息的,它会把所有消息保存下来,消费端根据 Topic 的 offset 数据来读取消息,因此消费端可以根据需要决定从哪里开始读取。
Kafka 后台可以配置策略,依据消息时间戳或者文件大小删除旧数据。
Page Cache
为了优化读写性能, Kafka 利用了操作系统本身的 Page Cache,就是直接利用堆外内存。
零拷贝
TODO
Kafka 的高可用架构
在 2.8.0 版本之前,Kafka 必须绑定 Zookeeter 启动,Zookeeter 负责存储 Kafka 集群的元数据。在 2.8.0 之后, Kafka 移除了对 Zookeeter 的强依赖,集群自身即可管理相关的数据。
参考: