Kafka专辑 : 生产者写入路径

生产者写入路径

必要前置知识

  • Java NIO:非阻塞 I/O,Selector 多路复用机制。
  • Java 内存模型:堆内存(Heap)与垃圾回收(GC)带来的 STW(Stop-The-World)风险。
  • CAP 理论简化:一致性(Consistency)与可用性(Availability)的权衡。

生产者写入链路全景:双线程架构

Kafka Producer 是典型的 "主线程-子线程" 异步架构,将"业务逻辑"与"网络 I/O"彻底解耦。

  • Main Thread(业务线程):负责拦截、序列化、分区,并将消息写入内存缓冲区。
  • Sender Thread(I/O 线程):负责从缓冲区"收割"批次,通过 NIO Selector 发送至 Broker。

Sender I/O 线程
内存缓冲区域
业务主线程
producer.send
Interceptor
Serializer
Partitioner
RecordAccumulator
Batch P0
Batch P1
BufferPool (内存池)
构建 Request
NIO Selector
Broker1
Broker2


关键组件深度解析

RecordAccumulator 与 BufferPool(内存池)

为了避免高频创建和销毁 byte[] 导致 Java Young GC 频繁,Kafka 设计了一套内存池机制。

  • BufferPool 机制 :Producer 启动时申请一块固定内存 buffer.memory(默认 32MB)。这块内存被切分成多个固定大小(batch.size,默认 16KB)的 Page。
  • 复用流程
  1. 当有新消息写入时,如果当前 Batch 还有空间,直接追加。
  2. 如果需要新 Batch,向 BufferPool 申请一个空闲 Page。
  3. Sender 线程发送完数据后,清空该 Page 并归还给 BufferPool,而不是让 JVM 回收。
  • 阻塞风险 :如果 buffer.memory 耗尽,send() 方法会阻塞,直到有内存归还或超时(max.block.ms)。

分区器与粘性分区(Sticky Partitioning)

分区器决定了消息去哪个 Partition。

  • 按 Key 分区Hash(key) % numPartitions。保证相同 Key 永远去同一分区(有序性)。
  • 无 Key(粘性分区):Kafka 2.4+ 的默认策略。
  • 机制 :随机选择一个分区,一直填,直到填满一个批次(batch.size)或超时(linger.ms,然后才切换到下一个分区。
  • 优势:相比旧版的轮询(Round-Robin),粘性分区能更快凑满 Batch,减少请求次数,显著降低延迟。

Sender 线程(NIO 模型)

Sender 是一个单线程 loop。

  • 它使用 Java NIO Selector 管理所有 Broker 的 TCP 连接。
  • 这意味着一个 Producer 实例只需少量线程就能维持与集群所有 Broker 的高效通信,不需要为每个 Broker 建立独立线程。

吞吐调优:批处理与压缩

核心理念:用 CPU 和内存换取网络带宽和磁盘 I/O

参数 含义 调优建议
batch.size 单个批次大小上限(默认 16KB) 建议调大到 32KB 或 64KB。太小会导致频繁网络请求;太大主要影响内存占用。
linger.ms 等待时间(默认 0ms) 最关键参数 。建议设为 5-10ms。让 Producer "等一等"以便凑出更大的 Batch。微小的延迟代价通常能换来 10 倍的吞吐提升。
compression.type 压缩算法 推荐 lz4 (极快)或 zstd(高压缩比)。压缩发生在 Batch 级别,Batch 越大,压缩效果越好。

可靠性体系:Acks、重试与超时

可靠性不仅仅是一个开关,而是一套组合拳。

acks 的三个级别

  • acks=0发后即忘。吞吐最高,数据丢失风险最大。
  • acks=1Leader 落盘即成功。Leader 挂掉且 Follower 未同步时会丢数据。
  • acks=all(或 -1):ISR 所有副本确认 。配合 min.insync.replicas > 1 使用,可靠性最高,但延迟最高。

超时双保险

  1. request.timeout.ms:单次网络请求的超时。超时后 Producer 会尝试重试。
  2. delivery.timeout.ms总超时 (默认 2 分钟)。从 send() 开始,包括所有重试、排队的时间。一旦超过这个时间,Producer 放弃并抛出异常。

进阶:幂等性与顺序性

网络抖动可能导致 Broker 没收到 Ack,Producer 重试,从而导致重复写入乱序

幂等性(Idempotence)

  • 开启enable.idempotence=true(Kafka 3.0+ 默认开启)。
  • 原理 :Producer 分配 PID,每条消息带序列号(Sequence Number)。Broker 发现 SN <= 已提交 SN 则视为重复,直接丢弃。
  • 限制 :只能保证单分区、单会话内的精确一次(Exactly-Once)。

乱序问题与 max.in.flight

如果允许 5 个请求同时在飞(In-Flight),请求 A 失败重试,B 成功,A 重试成功 -> 顺序变成 B, A(乱序)。

  • 未开启幂等 :必须设 max.in.flight.requests.per.connection = 1 才能保序(吞吐极低)。
  • 开启幂等(推荐) :可设 max.in.flight... <= 5
  • 原因 :Broker 会在内存中缓存乱序的 Batch,直到序号连续才落盘。既保证了顺序,又保证了并发吞吐。
相关推荐
java1234_小锋2 小时前
Zookeeper集群数据是如何同步的?
分布式·zookeeper·云原生
左灯右行的爱情3 小时前
Kafka专辑: 日志存储模型
分布式·kafka·linq
LB21123 小时前
Kafka笔记
分布式·kafka·linq
想你依然心痛5 小时前
Spark大数据分析与实战笔记(第六章 Kafka分布式发布订阅消息系统-02)
笔记·分布式·spark
胡萝卜的兔6 小时前
go-zero rpc 分布式 微服务
分布式·rpc·golang
小股虫1 天前
分布式事务:在增长中台,我们如何做到“发出去的内容”和“记录的数据”不打架?
分布式·微服务·云原生·架构·团队建设·方法论
是三好1 天前
分布式事务seata
java·分布式·seata
optimistic_chen1 天前
【Redis 系列】常用数据结构---Hash类型
linux·数据结构·redis·分布式·哈希算法
yuankunliu1 天前
【分布式事务】4、分布式事务Seata的高级应用详解
分布式