Kafka高性能设计_3_kafka


文章目录


1.消费消息的性能优化手段

Kafka高性能设计_3_java_02

1.1 稀疏索引

Kafka 利用offset 和 timestamp 查到消息。

B Tree 类的索引并不适用于 Kafka。哈希索引看起来却非常合适。

为了加快读操作,如果只需要在内存中维护一个「从 offset 到日志文件偏移量」的映射关系即可,每次根据 offset 查找消息时,从哈希表中得到偏移量,再去读文件即可。(根据 timestamp 查消息也可以采用同样的思路)。
但是哈希索引常驻内存,显然没法处理数据量很大的情况,Kafka 每秒可能会有高达几百万的消息写入,一定会将内存撑爆。

可我们发现消息的 offset 完全可以设计成有序的(实际上是一个单调递增 long 类型的字段),这样消息在日志文件中本身就是有序存放的了,我们便没必要为每个消息建 hash 索引了,完全可以将消息划分成若干个 block,只索引每个 block 第一条消息的 offset 即可,先根据大小关系找到 block,然后在 block 中顺序搜索,这便是 Kafka “稀疏索引” 的设计思想。

Kafka高性能设计_3_上下文切换_03
采用 “稀疏索引”,可以认为是在磁盘空间、内存空间、查找性能等多方面的一个折中。有了稀疏索引,当给定一个 offset 时,Kafka 采用的是二分查找来高效定位不大于 offset 的物理位移,然后找到目标消息。

1.2 mmap

利用稀疏索引可以高效查询, 可以通过 mmap(memory mapped files) 读写上面的稀疏索引文件。

mmap 和 page cache : 只有索引才用到mmap

常规的文件操作为了提高读写性能, 使用了page cache, 但是由于页缓存在内核空间, 不可以被用户直接寻址, 所以读取文件时候需要通过系统调用, 将页缓存中的数据拷贝到用户空间。

mmap就是可以使得磁盘文件和进程虚拟地址形成映射, 不会直接使用系统调用, 也不会使用额外的内存复制开销, 从而提高了文件读写效率

Kafka高性能设计_3_零拷贝_04
Kafka 的源码层面, 就是基于 JDK nio 包下的 MappedByteBuffer 的 map 函数,将磁盘文件映射到内存中。

log 文件不采用 mmap: mmap 有多少字节可以映射到内存中与地址空间有关,32 位的体系结构只能处理 4GB 甚至更小的文件。Kafka 日志通常足够大,可能一次只能映射部分,因此读取它们将变得非常复杂。然而,索引文件是稀疏的,它们相对较小。将它们映射到内存中可以加快查找过程,这是内存映射文件提供的主要好处。

1.3 零拷贝

将消息从磁盘文件中读出来, 然后通过网卡发送给消费者。

Kafka 用到了零拷贝(Zero-Copy)技术来提升性能。零拷贝是指数据直接从磁盘文件复制到网卡设备,而无需经过应用程序,减少了内核和用户模式之间的上下文切换。

不采用零拷贝的技术:4 次拷贝以及4 次上下文切换

Kafka高性能设计_3_java_05
采用零拷贝的技术: 3 次拷贝以及 2 次上下文切换

Kafka高性能设计_3_java_06

Kafka高性能设计_3_零拷贝_07

1.4 批量拉取

和生产者批量发送消息类似,消息者也是批量拉取消息的,每次拉取一个消息集合,从而大大减少了网络传输的 overhead。

生产者其实在 Client 端对批量消息进行了压缩,这批消息持久化到 Broker 时,仍然保持的是压缩状态,最终在 Consumer 端再做解压缩操作。
Kafka高性能设计_3_kafka_08

2.消费者组

2.1 消费者组的初始化流程

1.coordinator:辅助实现消费者组的初始化和分区的分配
coordinator节点的选择: grooupid的hash值% 50 (__consumer_offsets的分区数量)
例如: groupid的hash % 50 = 1, 那么就在1号分区上,

1.所有的consumer都发送joinGroup请求
2.coodinator选出一个consumer老大
3.condinator把所有的topic情况发送给leader。
4.leader制定消费计划
5.会把消费计划发给coodinator
6.coodinator把消费计划分给consumer,
7.每一个消费者和coodinator保持心跳, 默认为3s,如果超过时间默认为45s, session,timeout 的话会移除消费者组, 并触发再平衡。
如果消费者消费过长时间max.poll.interval.ms默认为5分钟,也会再平衡。

Kafka高性能设计_3_上下文切换_09

2.2 消费者组消费流程

1.consumer -> 发送sendFetchs抓取请求 -> send -> onSuccess 成功回调 -> 放入queue一个队列中 -> parseRecord -> Interceptors -> data

Kafka高性能设计_3_java_10