目录
KafkaProducer客户端三大重要组件
在KafkaProducer客户端中,有三个关键组件协同工作来完成消息的发送:
-
KafkaProducer:
- 这是用户与Kafka Producer交互的主要接口。通过调用
KafkaProducer.send()
方法,用户可以将消息封装为ProducerRecord
对象发送到Kafka集群。 - KafkaProducer负责序列化消息的Key和Value,并将它们封装成
ProducerRecord
对象。
- 这是用户与Kafka Producer交互的主要接口。通过调用
-
RecordAccumulator:
- RecordAccumulator是KafkaProducer内部的一个组件,它充当消息的缓冲区。当调用
KafkaProducer.send()
方法时,消息不是立即发送到Broker,而是先被缓存到RecordAccumulator中。 - RecordAccumulator管理着一个内存池,用于存储多个
ProducerBatch
。它根据消息的TopicPartition将消息分批存储,并在适当的时候触发批量发送。
- RecordAccumulator是KafkaProducer内部的一个组件,它充当消息的缓冲区。当调用
-
Sender:
- Sender是一个后台运行的子线程,负责从RecordAccumulator中取出
ProducerBatch
并发送到Kafka集群的Broker上。 - Sender线程会根据配置的参数(如
batch.size
和linger.ms
)来决定何时发送消息。它也负责处理来自Broker的响应,包括发送成功或失败的回调。
- Sender是一个后台运行的子线程,负责从RecordAccumulator中取出
RecordAccumulator 中的消息发送时机
在Kafka Producer中,RecordAccumulator
负责管理消息的缓存和批量发送。Sender线程通过特定的条件检查来确定何时从RecordAccumulator
中获取并发送消息。以下是Sender线程判断消息是否就绪并发送到Broker的流程:
-
遍历batches:
Sender线程首先遍历RecordAccumulator
中的batches
,这是一个ConcurrentMap<TopicPartition, Deque<ProducerBatch>>
,用于存储每个TopicPartition的消息批次(ProducerBatch
)队列。 -
检查批次状态:
对于每个TopicPartition
,Sender线程检查其对应的Deque<ProducerBatch>
队列,找到队列头部的ProducerBatch
。 -
查找Leader:
根据TopicPartition
信息,Sender线程查找其Leader副本所在的Broker节点。 -
未知Leader处理:
如果Leader信息未知(可能因为Leader变更或元数据未更新),则将该Topic添加到unknownLeaderTopics
集合,并计划发送元数据更新请求。 -
判断发送条件:
Sender线程判断当前ProducerBatch
是否满足发送条件,这包括:transactionCompleting
:是否事务正在完成。flushInProgress()
:是否正在进行缓存刷新。closed
:Producer是否已关闭。exhausted
:是否所有批次都已发送,没有更多消息可发送。waitedTimeMs >= timeToWaitMs
:自批次创建以来的时间是否已达到或超过linger.ms
配置的延迟。batch.isFull()
:批次是否已满,达到batch.size
配置的上限。
-
添加到readyNodes:
如果ProducerBatch
满足发送条件,且其Leader不在readyNodes
集合中,则将该Leader添加到readyNodes
中,表示已准备好发送。 -
计算下次检查延迟:
如果ProducerBatch
不满足发送条件,计算下一次检查的延迟时间,并更新nextReadyCheckDelayMs
。 -
发送消息:
对于所有在readyNodes
中的Leader,Sender线程将从对应的ProducerBatch
中获取消息并发送到Broker。 -
处理响应:
根据Broker的响应,Sender线程将处理消息发送的结果,包括成功确认或失败重试,并触发相应的回调。
Sender 发送消息流程
-
获取就绪批次:
Sender调用RecordAccumulator.drain()
方法,获取一系列准备好的ProducerBatch
列表。这些批次是根据readyNodes
中的Broker节点分组的。 -
遍历Broker节点:
遍历nodes
中的每个Broker节点,使用drainBatchesForOneNode()
方法抽取对应Broker上的Leader分区副本中的批次。 -
抽取逻辑:
使用drainIndex
和start
变量来记录遍历的进度,确保从每个Broker的0号分区开始抽取。 -
获取Leader分区信息:
根据Broker ID获取所有Leader分区副本信息。 -
循环抽取:
循环遍历每个分区,从RecordAccumulator
中抽取ProducerBatch
。 -
检查重试和大小限制:
如果ProducerBatch
正在重试并且尚未达到退避时间(backoff
),则跳过该分区。如果加上新批次后的消息总大小超过maxRequestSize
,则结束当前批次的抽取。 -
关闭批次:
将满足条件的ProducerBatch
加入到ready
列表中,并关闭该批次,禁止再向其中追加新消息。 -
封装ProduceRequest:
将ProducerBatch
列表封装成一个或多个ProduceRequest
,准备发送到Broker。 -
发送请求:
使用NetworkClient
发送ProduceRequest
到Broker节点。 -
管理InFlightRequests:
将发送的请求存储在InFlightRequests
队列中,这个队列的大小受max.in.flight.requests.per.connection
参数控制。 -
顺序保证:
如果enable.idempotence
设置为true
,Broker将保证请求的顺序处理。如果设置为false
,则需要将max.in.flight.requests.per.connection
设置为1以保证顺序。 -
处理响应:
KafkaProducer客户端通过Future
处理来自Broker端的响应,包括发送成功或失败。 -
内存回收:
发送完成后,将ProducerBatch
使用的ByteBuffer
回收到BufferPool
中,完成发送流程。