KafkaProducer 源码深度剖析:RecordAccumulator 封装消息流程

为什么需要 RecordAccumulator

前面几篇讲了 send() 怎么序列化、怎么选分区。但选完分区后,消息不是直接发出去的——那样每条消息一次网络请求,吞吐会惨不忍睹。

Kafka Producer 的核心优化就是攒批:同一分区的消息先在内存里攒成一个批次(默认 16KB),攒满了(或 linger.ms 到了)再一次性发给 Broker。RecordAccumulator 就是做这件事的。

它解决两个问题:

  • 攒批:多条消息合并成一个网络请求,减少 IO 次数
  • 路由:按分区组织,Sender 拉取时知道哪些分区有 ready 批次

01 数据结构:每个分区一个双端队列

RecordAccumulator 内部核心是一个ConcurrentMap<TopicPartition, Deque<ProducerBatch>>。每个分区对应一个 Deque,Deque 里是这个分区的所有批次。

为什么是 Deque 而不是普通 Queue?因为读写两端不同:

  • 写端(right):业务线程把新批次加到队尾,正在攒批
  • 读端(left):Sender 线程从队头拉 ready 批次发出去
  • 失败重传:如果一个批次发出去但 ACK 失败,它要重新入队——插到队头,保证尽快重试

Deque 正好支持两端操作。ConcurrentMap 保证不同分区的 Deque 之间并发安全——两个分区的业务线程同时 append,不用互相等。

除了 batches 这个 Map,还有一个incomplete: Set<ProducerBatch>。所有已经发出去但还没收到 ACK 的批次都在这个集合里。为什么要这个 Set?因为如果没有强引用,这些批次可能被 GC 回收——但网络上还飘着它们的字节,ACK 回来时找不到对应对象就炸了。

02 append() 流程:找尾批次,写不进去就新建

业务线程调accumulator.append()时,流程是一条决策链:

第一步:拿到该分区的 Deque。没有就新建一个。

第二步:看队尾最后一个批次。如果队尾批次还没满、没被 abort,就尝试往里写。

第三步:尝试写入。MemoryRecordsBuilder 把 key、value、timestamp 顺序写入 ByteBuffer。写得进去就返回。

第四步:写不进去(满了),分配新批次。从 BufferPool 里拿一块 ByteBuffer(默认 batch.size=16KB),新建 ProducerBatch,加到 Deque 队尾,把消息写进去。

第五步:内存不够。如果 BufferPool 里没有可用的 ByteBuffer,就阻塞等待,最多等 max.block.ms(默认 60 秒)。等不到就抛异常。

这里有个容易踩的坑:linger.ms=0 不代表消息立刻发出去。它只是说"攒满 batch.size 就发,不用等时间"。如果消息量很小,一个批次永远攒不满 16KB,就会一直等。线上低流量场景调大 linger.ms(比如 5ms)比调小 batch.size 更有效——让小批次攒得快一点,而不是把每个批次拆得更小。

03 ByteBuffer 布局:一条消息在内存里长什么样

一个 ProducerBatch 底层就是一块 ByteBuffer。写入格式是 RecordBatch v2:

[Batch Header(公共头)] baseOffset | length | magic(=2) | crc attributes | lastOffsetDelta | baseTimestamp | maxTimestamp [Record 1] length | attributes | timestampDelta | keyLength | key | valueLength | value [Record 2] ... [Record N] ... [剩余空闲空间]

几个关键设计:

为什么用 timestampDelta 而不是绝对时间戳?同一批次的消息时间戳相差很小(默认在同一毫秒或几毫秒内),存差值(varint 变长整数)比存 8 字节绝对时间省得多。baseTimestamp 存在 Batch Header 里只存一次。

为什么在 batch 级别压缩而不是单条压缩?同一个 batch 里的消息通常相似(同一个用户、同一个事件类型),batch 级别压缩率远高于逐条压缩。gzip 能压到原来的 1/5,zstd 更好。

单条消息超过 batch.size 怎么办?直接分配一块单独的 ByteBuffer,大小就是这条消息的大小(受 max.request.size 限制,默认 1MB)。它自己成一个 batch,不需要和其他消息凑。

04 内存管理:BufferPool 和背压

RecordAccumulator 不是无限内存。总大小由 buffer.memory 控制(默认 32MB)。所有批次共用这 32MB。

BufferPool 管理这些内存:分配 ByteBuffer、回收 ACK 后的 ByteBuffer。当 32MB 用完了,新的 append 请求就阻塞——这就是背压。

线上最常见的内存问题:

  • 消息太大:单条消息 10MB,超过 buffer.memory,直接 OOM。调 max.request.size 和 buffer.memory。
  • 发送太慢:Broker 响应慢,批次都在 incomplete 集合里不释放,32MB 用完,业务线程阻塞 60 秒后抛异常。这是 Broker 侧问题,不是 Producer 配置问题。
  • 分区太多:每个分区都攒一个批次,32MB 被切成 100 个批次,每个批次只有 320KB——比 batch.size=16KB 大得多但利用率低。

经验值:buffer.memory 至少是 batch.size × 活跃分区数 × 2。比如 50 个分区,每个 16KB,32MB 够用;如果 500 个分区,就要调到 160MB。

05 写在最后

RecordAccumulator 看起来就是个 Map,但它是 Producer 性能的核心:攒批减少网络请求,Deque 支持失败重传,BufferPool 控制内存上限。理解了 append() 流程,就理解了为什么调 batch.size、linger.ms、buffer.memory 能影响吞吐——它们分别控制"批次多大"、“等多久”、“总共多少内存”。

相关新闻

HTML5视频播放实战:从video标签到编码兼容与性能优化

HTML5视频播放实战:从video标签到编码兼容与性能优化

1. 先搞清楚&#xff1a;为什么视频不能直接塞进网页1.1 从GIF到HTML5视频的演进前几年还经常能看到有人用GIF做演示动效&#xff0c;一个十几秒的录屏转成GIF之后动辄几十MB&#xff0c;加载起来页面卡得不行。GIF本质上是图像格式&#xff0c;不是为连续画面临时设计的&#…

2026/10/21 5:16:06 阅读更多 →
朴素贝叶斯与SVM同场竞技:垃圾邮件识别Python工程全解析

朴素贝叶斯与SVM同场竞技:垃圾邮件识别Python工程全解析

简介&#xff1a;这是一个基于朴素贝叶斯与支持向量机&#xff08;SVM&#xff09;的垃圾邮件识别系统&#xff0c;适合机器学习入门者、毕业设计及分类项目实战人群。项目通过对正常邮件和垃圾邮件进行语料训练&#xff0c;完成词频统计与模型对比&#xff0c;实现邮件自动分类…

2026/10/21 5:16:06 阅读更多 →
Ollama+通义千问本地部署大模型:三步实操与避坑指南

Ollama+通义千问本地部署大模型:三步实操与避坑指南

这两年AI大模型算是彻底走进普通开发者的视野了。不过很多人对“本地部署”这件事一直有种错觉&#xff0c;觉得要么需要一台几万块的服务器&#xff0c;要么得折腾CUDA、Docker、各种依赖环境&#xff0c;最后把自己劝退。直到我试了Ollama加通义千问这条组合路线&#xff0c;…

2026/10/21 5:16:06 阅读更多 →

最新新闻

Elasticsearch集群安全配置:TLS/SSL与密钥库管理全解析

Elasticsearch集群安全配置:TLS/SSL与密钥库管理全解析

给Elasticsearch集群做安全配置这件事&#xff0c;远比想象中麻烦。很多人以为在elasticsearch.yml里加几行TLS/SSL配置就能收工&#xff0c;结果往往是节点之间互相不认证书、Kibana连不上、客户端报handshake failed&#xff0c;最后只能回滚。我这次把一套三节点ES集群从裸H…

2026/10/21 5:49:18 阅读更多 →
嘎嘎降AI实战教程:从注册到多轮降重,让AI文本更自然

嘎嘎降AI实战教程:从注册到多轮降重,让AI文本更自然

"嘎嘎降AI"这名字听起来有点随意&#xff0c;但用起来确实能解决一个实际问题&#xff1a;AI生成的内容&#xff0c;如何改得更像人写的。很多朋友现在写材料、做方案、写公众号&#xff0c;第一稿都习惯让AI对话软件先起个草&#xff0c;然后再自己改。可草稿一到手…

2026/10/21 5:49:18 阅读更多 →
广播、未知、同网、异网:交换机转发与排障核心解析

广播、未知、同网、异网:交换机转发与排障核心解析

干网络这行久了&#xff0c;最常被新人追问的一个问题是&#xff1a;交换机把一个帧从某个端口收进来之后&#xff0c;它凭什么决定从哪个端口送出去&#xff1f;教科书上会讲查MAC地址表、泛洪、丢弃&#xff0c;但真到排障时&#xff0c;大家还是容易懵。后来我发现&#xff…

2026/10/21 5:49:18 阅读更多 →
嘎嘎降AI工具详解:从注册到实操的AI文本自然化处理指南

嘎嘎降AI工具详解:从注册到实操的AI文本自然化处理指南

“你手头有一段AI生成的文章&#xff0c;第一眼读起来特别工整&#xff0c;逻辑也没什么毛病&#xff0c;但总觉得有点‘板’&#xff0c;像戴着面具在说话”——这就是我最初接触到“嘎嘎降AI”这个工具时最想解决的问题。它不是用来做文案初稿的&#xff0c;也不是单纯改错别…

2026/10/21 5:49:18 阅读更多 →
技能管理系统:从技能地图到刻意练习的完整框架

技能管理系统:从技能地图到刻意练习的完整框架

很多人看到“skills”这个词&#xff0c;第一反应是“又一篇讲职场技能提升的文章”。但我今天想换个角度聊&#xff1a;技能不是一个名词&#xff0c;而是一套需要持续维护的系统。你收藏过一堆课程、买过一堆书、报过训练营&#xff0c;最后真正变成自己能力的&#xff0c;其…

2026/10/21 5:49:18 阅读更多 →
AI信息挖掘实战:从提问到交叉验证,把碎片信息串成认知

AI信息挖掘实战:从提问到交叉验证,把碎片信息串成认知

元旦的钟声刚敲过&#xff0c;我猜不少人正把“新的一年多学点东西、把手头的事做精”写进计划表。这几天我发现一个很有意思的现象&#xff1a;大家不是不想查资料&#xff0c;而是被资料淹没了。打开浏览器想看某个行业的最新动态&#xff0c;结果十条链接里八条是去年的&…

2026/10/21 5:48:18 阅读更多 →

日新闻

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

这段时间硬件圈和AI圈都在传一个事&#xff1a;AMD锐龙AI Max PRO 400系列把最高192GB统一内存搬进了PC形态的产品里&#xff0c;按宣传口径&#xff0c;本地跑320B参数的大家伙不再是纸面概念。我第一反应倒不是“内存真大”&#xff0c;而是这话背后的含金量——它把过去只能…

2026/10/21 0:00:11 阅读更多 →
AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

1. 从“封号重开”这个行为说起&#xff1a;用户到底在追求什么“封号重开”这四个字&#xff0c;放在任何产品语境里都带着一股决绝的味道。一个账号从零开始积累使用记录、调教偏好、沉淀工作流&#xff0c;背后是大量的时间和精力成本。正常情况下&#xff0c;没有人愿意主动…

2026/10/21 0:00:11 阅读更多 →
ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

1. 为什么在ROS里硬刚YOLOv3不是“炫技”&#xff0c;而是工程落地的必经之路我第一次在某高校机器人实验室看到那台搭载Jetson TX2的移动底盘原地打转、反复识别错垃圾桶时&#xff0c;就意识到&#xff1a;单纯跑通darknet_ros demo和真正让YOLOv3在ROS里“干活”&#xff0c…

2026/10/21 0:00:11 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/20 20:43:59 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/21 4:27:17 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →