当前位置: 首页 > news >正文

Kafka底层解析:可靠性与高性能原理

目录

Kafka特点

1.kafka通信协议解析

2.Kafka可靠性底层原理(副本机制)

3.Kafka存储底层原理(磁盘存储)

4.kafka高性能原因(顺序写入+零拷贝+批处理)

5.kafka如何配合操作系统pageCache实现高性能?


Kafka特点

首先先认识kafka特点,再学习对应特点是如何实现的

1.可靠性,通过不同分区之间的同步机制实现可靠性

2.可扩展性,kafka无需停机就可以扩展节点以及节点上线

3.持久性:数据存储到磁盘上,持久性存储

4.性能:kafka具有高吞吐量打到TB级别的数据,也有非常稳定的性能

5.速度快,顺序写入和零拷贝技术是的kafka延迟控制在毫秒级别

1.kafka通信协议解析

首先这个协议没有明确的名称,一般叫做kafka二进制通信协议

一、Kafka 通信协议的本质

Kafka 协议是 基于 TCP 之上的自定义二进制协议,而非 HTTP 等文本协议。其核心设计目标是:高效、紧凑、低开销,适配高频消息传输场景。

  • 客户端与 Broker、Broker 之间的通信均通过该协议完成。
  • 协议采用 请求 - 响应(Request-Response)模式,但基于长连接实现,避免了 HTTP 短连接的频繁握手开销。

二、协议的核心结构

Kafka 协议的每一条消息(请求或响应)都遵循固定的二进制格式,整体分为三部分:[长度前缀] + [协议头] + [协议体]

1. 长度前缀(Length Prefix)
  • 作用:标识整个消息(协议头 + 协议体)的总字节数,用于接收方解析时界定消息边界。
  • 格式:4 字节无符号整数(大端序,即网络字节序),最大值为 2^31 - 1(约 2GB,限制单条消息大小)。
2. 协议头(Header)

请求和响应的头部结构不同,分别包含必要的元数据:

类型

字段

含义

请求头

api_key

标识请求类型(如生产消息、消费消息、创建 Topic 等,每个操作对应唯一值)

api_version

协议版本号(用于兼容不同版本的 Kafka 集群)

correlation_id

客户端生成的唯一 ID,用于关联请求与响应(异步通信时匹配响应归属)

client_id

客户端标识(可选,用于集群监控和日志追踪)

响应头

correlation_id

与请求的 correlation_id 一致,用于匹配

error_code

错误码(0 表示成功,非 0 表示失败,携带错误信息)

3. 协议体(Payload)

根据请求类型(api_key)的不同,协议体包含具体的业务数据,例如:

  • 生产消息(ProduceRequest):包含 Topic 名、分区号、消息批次(RecordBatch)等。
  • 消费消息(FetchRequest):包含消费组 ID、Topic 分区、起始偏移量(offset)、最大字节数等。
  • 元数据请求(MetadataRequest):包含需要查询的 Topic 列表,用于获取集群元数据(如分区分布、Leader 位置等)。

协议体的字段通过 紧凑的二进制编码 实现(如变长整数、字符串前缀长度等),避免冗余开销。

三、协议的交互机制

  1. 长连接复用客户端与 Broker 建立 TCP 长连接后,会在该连接上持续发送多个请求(如 Producer 批量发消息、Consumer 拉取消息),无需每次通信重新握手,大幅降低连接成本。
  2. 异步非阻塞通信基于 Java NIO(Selector + Channel)实现,单线程可处理上千个连接(Reactor 模型):
    • 客户端发送请求后无需阻塞等待响应,可继续发送其他请求。
    • 服务端通过事件驱动处理请求,响应通过同一连接异步返回,客户端通过 correlation_id 匹配请求与响应。
  1. 批量与压缩
    • 协议支持批量传输(如 Producer 将多条消息打包成 RecordBatch),减少请求次数。
    • 消息批次可通过 GZIP、Snappy 等算法压缩,协议体中包含压缩标识,服务端解压后再处理。
  1. 版本兼容通过 api_version 字段实现协议版本控制:
    • 高版本客户端可与低版本 Broker 通信(自动降级到兼容版本)。
    • 新增功能通过新的 api_key 或字段扩展,不影响旧版本解析。

四、为什么不使用 HTTP 或 UDP?

  1. 对比 HTTP
    • HTTP 是文本协议,头部(如 Content-TypeCookie 等)冗余信息多,对于高频小消息场景,头部开销可能远超消息本身,效率低。
    • HTTP 基于 “请求 - 响应” 模型,但天然适合短连接,长连接(如 HTTP/2)的设计复杂度高于 TCP 直接复用,且仍有协议栈开销。并且Http一般是视为单向的,并不满足Broker有时需要向客户端推送关键控制信息,而不是主动响应客户端的请求。例如消费者组发生变换(reblance的时候)需要主动通知所有消费者,触发再平衡流程。以及消费者定期向Broker发送心跳以及维持会话
    • Kafka 需要双向自由通信(如 Broker 向 Consumer 推送再平衡通知),HTTP 的客户端主动请求模式不适合。
  1. 对比 UDP
    • UDP 无连接、不可靠(可能丢包、乱序),而 Kafka 作为消息队列,可靠性是核心诉求(如消息不丢失、分区内有序),UDP 无法保证。
    • UDP 缺乏流量控制机制,在高吞吐场景下易导致网络拥塞,而 TCP 的滑动窗口机制可动态调节发送速率,适配网络状况。

五、协议如何利用 TCP 特性?

  1. 可靠传输(ACK 机制)TCP 的三次握手、超时重传、校验和等机制保证了协议消息的不丢失、不损坏,为 Kafka 消息的可靠性提供底层支撑。
  2. 顺序性TCP 保证字节流的顺序传输,与 Kafka 分区内消息的顺序性(按 offset 递增)天然契合,避免消息乱序。
  3. 长连接与流量控制
    • TCP 长连接减少了连接建立 / 关闭的开销(三次握手、四次挥手),适配 Kafka 持续通信的场景。
    • TCP 滑动窗口机制可根据接收方的处理能力动态调整发送速率,避免 Broker 被客户端消息压垮。
  1. 拥塞控制TCP 的慢启动、拥塞避免等算法可根据网络拥堵情况自动调节发送速率,保证集群在复杂网络环境下的稳定性。

2.Kafka可靠性底层原理(副本机制)

kafka可靠性依赖副本模式,ISR,磁盘持久化存储以及故障恢复实现

1. 副本机制(Replication)

  • 每个 Topic 分为多个 Partition。
  • 每个 Partition 可配置多个副本(replication factor),分布在不同 Broker 上。
  • 其中一个副本是 Leader,负责处理所有读写请求。
  • 其余副本是 Follower,通过 Pull 模式从 Leader 同步数据。

📌注意:Kafka 使用 Pull 模式(Follower 主动拉取),而不是 Push。这避免了 Leader 因网络波动导致重试压力过大。


2. ISR(In-Sync Replicas)同步副本集

首先我们需要弄清楚,ISR!=follower集合,ISR是leader+保持同步的follower集合

ISR 是 Kafka 实现“高可用 + 不丢数据”的核心机制。

ISR 包含哪些副本?
  • 当前与 Leader 保持同步 的副本集合。
  • 判断标准:
    • Follower 在 replica.lag.time.max.ms(默认 30s)内发送过心跳。
    • Follower 没有严重落后(旧版本看消息数,新版本主要看时间)。
关键配置:acks

acks

行为

可靠性

0

不等待任何确认

最低,可能丢数据

1

等待 Leader 写入成功

中等,Follower 可能未同步

all

-1

等待 ISR 中所有副本写入成功

最高,推荐生产使用

acks=all 并不等所有副本,而是等 当前 ISR 中的副本。如果某个 Follower 落后被踢出 ISR,就不需要等它。


如果我们希望kafka消息存储端保持高可靠性,需要黄色至ack=all,虽然性能会有所下降

3. 故障恢复与 Leader 选举

注意,controller只存在于一个broker节点中哦

  • Kafka 集群中有一个 Controller Broker(由 ZooKeeper 或 KRaft 选举产生)。
  • 当 Leader 宕机,Controller 会从 ISR 中选出新的 Leader
  • 新 Leader 必须拥有最新的数据(因为来自 ISR),避免数据丢失。

如果 ISR 为空(所有副本都挂了),Partition 将不可用(如果 unclean.leader.election.enable=false)。

同时注意,选举leader的一个规则,优先级从上到下

1.手动指定的优先级

2.副本是否在线

3.数据完整性,优先选择LEO(日志末端偏移量)最大的副本,也就是最近和leader同步过的副本


4. Unclean Leader Election

  • unclean.leader.election.enable=true:允许从非 ISR 中选 Leader,提高可用性,但可能丢数据。
  • 生产环境建议设为 false,优先保证一致性。


Kafka 通过 多副本 + ISR + acks=all 实现了在部分节点故障时仍能保证数据不丢失,同时不影响服务可用性。

3.Kafka存储底层原理(磁盘存储)

Kafka 的数据存储在磁盘上,但性能却很高,这是因为它做了大量优化。

1. 日志文件结构(Log Segment)

每个 Partition 对应一个 日志(Log),日志被分为多个 Segment 文件

Kafka 将每个分区(Partition)的日志拆分为多个 Segment(段),每个 Segment 是一组命名规则统一的文件(.log.index.timeindex),这是存储优化的基础。

text编辑/topic-name-0/
├── 00000000000000000000.index  ← 稀疏索引
├── 00000000000000000000.log     ← 实际消息数据
├── 00000000000000000000.timeindex ← 时间索引
├── 00000000000000000987.index
├── 00000000000000000987.log
└── ...
  • .log:实际消息数据,按 offset 顺序追加写入
  • .index:偏移量索引,稀疏索引,记录 offset → 文件物理位置的映射
  • .timeindex:时间戳索引,支持按时间查找消息

📌 例如:00000...987.log 表示这个 Segment 从 offset 987 开始


顺序写

Kafka 将消息顺序写入文件末尾(append),顺序写对磁盘(尤其传统 HDD)有巨大的性能优势;即使是 SSD,顺序写也更少产生随机写导致的擦写放大。操作系统会把写入先放进 page cache,然后异步刷到磁盘,减少直接磁盘 I/O 阻塞。顺序写 + 大批量写的组合显著提升了吞吐。

批量与压缩

Producer 会把多条消息合并成 batch,batch 内可以压缩后写入磁盘/网络。批量化减少了每条消息的协议和 I/O 开销;压缩降低了网络带宽与磁盘使用,尽管压缩/解压有 CPU 开销,但通常对吞吐是有利的。

2. 稀疏索引(Sparse Index)

  • 不是每条消息都建索引,而是每隔一定字节或消息数建一个索引项。
  • 查找某 offset 时:
    1. 用二分查找找到对应的 Segment
    2. .index 文件中找到最近的索引项
    3. 从该位置开始顺序扫描少量消息即可定位

减少索引大小,加快查找速度


3. mmap(内存映射文件)

  • Kafka 使用 mmap 系统调用将文件映射到内存。
  • 读写文件就像操作内存一样,由操作系统管理页缓存。
  • 避免频繁的 read/write 系统调用开销。

注意:mmap 在大文件时可能引发 page fault 性能问题,Kafka 后期版本已部分改用 sendfile + transferTo


4. 日志清理策略

  • delete:按时间或大小删除旧 Segment(默认)
  • compact:保留每个 key 的最新值,适用于状态类数据(如用户信息)
  • 关于删除旧segement可以通过手动设置保存最久是多久之前的,允许按照事件设置

存储总结
通过 分段日志 + 稀疏索引 + mmap,Kafka 实现了高效的大规模数据存储与快速查找。

4.kafka高性能原因(顺序写入+零拷贝+批处理)

Kafka 能达到 百万级 QPS,关键在于三大技术:

1. 顺序写入磁盘(Sequential Write)

  • 消息只能追加到日志末尾,不修改历史数据
  • 磁盘顺序写速度接近内存随机写(可达 600MB/s)
  • 对比:数据库随机写入需要寻道,慢得多
  • 其实就是顺序写入每个segment的文件末尾,到达一定空间就写下一个文件

Kafka 把“慢”的磁盘变成了“快”的队列


2. 零拷贝(Zero-Copy)技术

传统文件传输路径(4次拷贝 + 4次上下文切换):

textDisk → Kernel Buffer → User Buffer → Socket Buffer → NIC Buffer

四次详细过程

  1. 第一次次拷贝:磁盘 → 内核缓冲区(PageCache)(通过DMA)
    • 应用程序调用 read() 系统调用,请求读取文件数据。
    • 操作系统内核发起动磁盘 I/O,将文件数据从磁盘读取到内核态的缓冲区(通常是 PageCache,操作系统管理的内存缓存)。
    • 上下文切换 1:用户态 → 内核态(read() 系统调用触发)。
  1. 第二次拷贝:内核缓冲区 → 用户态缓冲区
    • 内核将 PageCache 中的数据拷贝到应用程序的用户态缓冲区(如应用程序自己维护的字节数组)。
    • 上下文切换 2:内核态 → 用户态(read() 调用返回,数据已到用户态)。
  1. 第三次拷贝:用户态缓冲区 → 内核 Socket 缓冲区
    • 应用程序调用 write() 系统调用,将用户态缓冲区的数据发送到网络。
    • 内核将用户态缓冲区的数据拷贝到内核态的 Socket 缓冲区(专门用于网络传输的内核缓存)。
    • 上下文切换 3:用户态 → 内核态(write() 系统调用触发)。
  1. 第四次拷贝:内核 Socket 缓冲区 → 网络适配器(NIC)缓冲区
    • 内核通过 DMA(直接内存访问)将 Socket 缓冲区的数据拷贝到网络适配器的硬件缓冲区(NIC Buffer),最终通过网络发送。
    • 上下文切换 4:内核态 → 用户态(write() 调用返回,数据已提交到网络层)。

Kafka 使用 sendfile() 系统调用实现零拷贝:

c编辑sendfile(out_fd, in_fd, &offset, count);

零拷贝路径(1次拷贝 + 2次上下文切换):

text编辑Disk → Kernel Buffer → NIC Buffer (DMA)
  • CPU 几乎不参与数据搬运
  • 大幅降低 CPU 使用率和延迟

Linux 的 transferTo() 方法(Java NIO)就是封装了 sendfile


详细过程

一、sendfile() 零拷贝的核心流程

sendfile(out_fd, in_fd, &offset, count) 的作用是直接在内核态完成数据从文件(in_fd)到网络套接字(out_fd)的传输,无需应用程序中转。其数据路径仅包含 1 次显式拷贝2 次上下文切换

  1. 第一步:磁盘 → 内核缓冲区(PageCache)
    • 当 Kafka 需要读取 .log 文件中的消息时,操作系统通过 DMA(直接内存访问)将磁盘数据加载到内核态的 PageCache(页缓存)中(这一步是所有文件读取的基础,无法避免)。
  1. 第二步:内核缓冲区 → 网卡缓冲区(NIC Buffer)
    • sendfile() 系统调用触发内核直接将 PageCache 中的数据拷贝到网卡的硬件缓冲区(NIC Buffer),同样通过 DMA 完成(无需 CPU 参与)。
    • 这里的 “1 次拷贝” 指的就是 PageCache → NIC Buffer 这一步(内核态内部的拷贝,无用户态参与)。
二、上下文切换:仅 2 次
  • 第一次切换:用户态 → 内核态(应用程序调用 sendfile() 系统调用时)。
  • 第二次切换:内核态 → 用户态(sendfile() 执行完成,返回结果给应用程序时)。

相比传统方式的 4 次切换,sendfile() 减少了一半的切换开销,降低了 CPU 资源消耗。

三、为什么称为 “零拷贝”?

严格来说,sendfile() 并非 “完全零拷贝”(仍有内核态内部的 1 次拷贝),但之所以被称为 “零拷贝”,是因为它消除了用户态与内核态之间的 2 次冗余拷贝(传统方式中 “内核缓冲区 → 用户缓冲区” 和 “用户缓冲区 → Socket 缓冲区” 这两步)。

3. 批处理(Batching)

  • 生产者:收集多条消息打包成 Record Batch 发送
  • 网络传输:减少小包数量,提高吞吐
  • 压缩:支持 Snappy、GZIP、LZ4、ZSTD,压缩整个 Batch
  • 消费者:一次拉取多条消息(fetch.min.bytes

4. 页缓存(PageCache)利用

  • Kafka 依赖操作系统 PageCache,而不是自己管理缓存
  • 热数据自动留在内存,读取极快
  • 写入时先写入 PageCache,后台异步刷盘(flush

避免 JVM 堆内存压力和 GC 问题


高性能总结
Kafka 通过 顺序写 + 零拷贝 + 批处理 + PageCache,充分发挥了现代操作系统和硬件的性能,实现了远超传统消息队列的吞吐量。

5.kafka如何配合操作系统pageCache实现高性能?

一、什么是 PageCache

简单定义

PageCache 是 Linux 内核中最重要的文件系统缓存,用于缓存文件内容的内存页(Page)
所有通过文件系统(如 ext4、xfs)读写的数据,都会经过 PageCache

它的目标就是:

让频繁访问的文件内容留在内存中,不用每次都访问磁盘。

Kafka 不自己维护缓存,而是完全依赖操作系统的 PageCache

Kafka 写入过程中的 PageCache 利用

让我们看一下 生产者写消息时的底层路径:

  1. Producer 发送消息 → Broker 接收并写入分区日志文件;
  2. Broker 线程通过 FileChannel.append() 写入文件末尾;
  3. 操作系统将数据写入 PageCache(写缓冲区)
  4. 立即返回成功(异步刷盘)
  5. 后台线程周期性或按需调用 fsync 将 PageCache 数据刷入磁盘

所以,Kafka 写磁盘其实是:

写入 PageCache,而不是马上落盘。

优点:

  • 超快(相当于内存写入速度);
  • 数据安全性仍可控制(acks=all + replica + fsync)。

例:Kafka 写入日志段 segment 时

假设 topic-A 的一个分区日志段文件是:

/kafka-logs/topic-A-0/00000000000000000000.log

写入消息的时候

FileChannel channel = new FileOutputStream(file, true).getChannel();
channel.write(buffer);
  • 这里 write() 不会直接写磁盘;
  • Linux 内核会将数据缓存在 PageCache;
  • 之后由内核根据内存压力或 Kafka 调用 fsync() 时真正刷盘。

Kafka 读取(消费者消费)时的 PageCache 利用

消费者拉取消息时,Broker 会读取这个 .log 文件。
此时的路径是:

  1. 内核检测到数据已在 PageCache 中(命中缓存);
  2. 不需要再从磁盘读;
  3. Kafka 使用 FileChannel.transferTo() 直接触发 零拷贝
  4. 数据直接从 PageCache → Socket Buffer → 网卡
  5. 不经过 JVM 用户态内存。

倘若要读的数据不在pagecache是不是就不能零拷贝?

内核执行 sendfile() 时的流程大致如下(简化):

  1. 检查文件对应的页是否在 PageCache
  2. 如果命中 → 直接从 PageCache 拿数据;
  3. 如果未命中 → 异步从磁盘读取数据到 PageCache;
  4. 一旦页准备好,内核通过 DMA 直接把 PageCache 中的页数据拷贝到 NIC(网卡缓冲区)
  5. 数据从 Socket 发送出去。

其实还是能零拷贝的

就这样,配合kafka底层协议,文件存储,pageCache等知识点不知有没有帮你对kafka理解加深呢?

http://www.dtcms.com/a/506856.html

相关文章:

  • 分布式链路追踪中的上下文传播与一致性维护技术
  • 为已有nextjs项目添加supabase数据库,不再需要冗余后端
  • 网站建设怎样上传程序微信网站搭建多少钱
  • rabbitmq在微服务中配置监听开关
  • 下一代时序数据库标杆:Apache IoTDB架构演进与AIoT时代的数据战略
  • k8s中的控制器
  • Blender入门学习02
  • 动态规划的“数学之魂”:从DP推演到质因数分解——巧解「只有两个键的键盘」
  • Blender入门学习01
  • 网站开发word文档精品简历模板网站
  • WrenAI:企业级AI数据分析平台技术解析
  • 【Processing】椭圆眼珠鼠标跟随
  • 工业显示器在矿用挖掘机中的应用
  • 济南企业网站开发网站建设域名
  • 【深度学习计算机视觉】14:实战Kaggle比赛:狗的品种识别(ImageNet Dogs)
  • 基于k8s的Python的分布式深度学习训练平台搭建简单实践
  • 网站服务器地址在哪里看前端工程师是做网站吗
  • 基于SpringBoot的环保行为记录与社区互动平台(Vue+MySQL)
  • 洛谷 P3392 涂条纹-普及-
  • 【 柒个贰航空旅游-注册安全分析报告-无验证方式导致安全隐患】
  • CentOS 7 安装 MySQL 8
  • Java 数据类型分类
  • 定制高端网站建设设计上传网站图片不显示
  • 无人机路径规划与定位技术原理及实现详解
  • 自己做公司网站适用于手机的网站怎么建设
  • 解决前端多标签页通信:BroadcastChannel
  • [css] border 渐变
  • 前端错误监控实践:Sentry 在 Vite + Vue 项目中的配置与原理详解
  • Marin说PCB之GMSL2网络中AC电容前端控制100欧姆和不做差分100欧姆的区别?
  • Oracle 数据库 Schema 备份与导入全攻略