资讯动态

KafkaProducer 源码深度剖析:处理暂存状态的响应消息

发布时间:2026/10/27 18:40:39 来源:尧图企业网站定制
前情回顾与本篇视角前两篇讲了网络层的粘包和拆包——Selector 怎么从 TCP 字节流中还原出完整的响应帧。但还原出响应只是第一步响应本身不携带请求体只有一个 correlationId。Kafka 怎么知道这个响应对应哪个请求怎么触发业务方的 callback这就是 InFlightRequests 的工作。InFlightRequests 是整个异步发送模型的核心基础设施请求发出后暂存在这里响应到达后按 correlationId 匹配触发 callback处理成功或失败。01 InFlightRequests暂存请求的结构请求发出时NetworkClient.doSend() 做三件事分配自增的 correlationId、构造 ClientRequest、加入 InFlightRequests然后写入通道。InFlightRequests 结构MapNode, DequeClientRequest按目标节点分组的双端队列。每个节点一个队列FIFO 顺序。max.in.flight.requests.per.connection默认5限制每节点在途请求数。ClientRequest 暂存的关键信息correlationIdint32 自增 ID请求唯一标识响应中带回用于匹配callbackRequestCompletionHandler响应到达时触发 onComplete(metadata, exception)createdTimeMs请求发出时间戳用于超时检测destination / request目标节点和原始请求体重试时重新发送响应匹配流程Selector.poll() 读到完整响应 → 从响应头取出 correlationId 和 source → 从该节点 InFlight 队首取出 ClientRequestFIFO→ 校验 correlationId 一致 → 解析响应体 → 触发 callback.onComplete()。02 响应处理的三种结果解析 ProduceResponse 后检查每个 partition 的 errorCode分三种情况处理。成功errorCode0batch.done(baseOffset, timestamp)设置偏移量和时间戳FutureRecordMetadata.complete()业务线程的 future.get() 返回callback.onComplete(metadata, null)触发用户回调exceptionnullInFlight 计数减 1可以发送新请求可重试失败RetriableException如 NOT_LEADER_FOR_PARTITION、CORRUPT_MESSAGE 等临时错误请求放回 InFlight 队首下次 poll 重新发送重试次数 1同时触发元数据刷新leader 可能变了重试上限双条件attempts retries 且 now - createdTimeMs request.timeout.ms不可重试失败NonRetriableException如 INVALID_TOPIC_EXCEPTION、MESSAGE_TOO_LARGE 等配置或数据错误batch.done(exception)Future 以异常完成callback.onComplete(null, e)业务回调收到异常InFlight 计数减 1不重试关键参数retries默认 int 最大值、retry.backoff.ms默认100ms、request.timeout.ms默认30000ms、max.in.flight默认5、enable.idempotence开启后 max.in.flight≤5、acks0/1/all。03 暂存请求的生命周期与超时检测请求从发出到完成经历 T0→T1→T2→T3 四个阶段。但响应可能永远不到达——超时或连接断开时Kafka 怎么处理暂存请求正常路径T0 请求发出createdTimeMsnow加入 InFlight→ T1 写入通道selector.send()→ T2 响应到达NetworkReceive.done()匹配 InFlight 队首→ T3 触发 callbackbatch.done()Future 完成从 InFlight 移除。超时检测每轮 poll() 调用 maybeThrowTimeoutException()遍历所有节点的 InFlight 队列。只检查队首最老的请求now - createdTimeMs request.timeout.ms → 超时。队首超时则整个队列的请求都失败FIFO 顺序保证触发 callback.onComplete(null, TimeoutException)。连接断开read 返回 -1 或 IOException → 连接断开。mute 该节点所有在途请求标记失败可重试的放回队首不可重试的抛异常。ConnectionStates 标记 DISCONNECTED下次 poll 尝试重连。为什么需要暂存状态请求和响应解耦发送时不需要同步等待Sender 可以继续发其他请求correlationId 匹配响应不带请求体靠 ID 找回原始请求和 callback重试支持失败时保留原始请求体重新发送不需要业务层重构超时检测记录 createdTimeMs每轮 poll 检查避免请求永久挂起流量控制max.in.flight 限制在途请求数防止 Broker 过载顺序保证同一节点队列 FIFO响应按顺序匹配保证消息顺序幂等性配合 PID sequence number重试不会重复写入内存可控在途请求数有上限不会无限累积导致 OOM写在最后InFlightRequests 看起来只是个「存请求等响应」的队列但它是整个异步发送模型的核心。correlationId 匹配、三种结果处理、双条件重试上限、超时检测、连接断开恢复——每一个边界情况都有对应的处理机制。这也是读源码的价值不是看用了什么复杂数据结构而是看怎么用简单的队列 自增 ID 把异步通信的边界情况处理干净。下一篇看 Producer 端的完整异常处理体系——同步异常和异步异常怎么分类、怎么传递给业务方。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑