整体架构
生产过程由两个线程协调运行,分别为主线程和sender线程(发送线程)。
主线程中,由KafkaProducer创建消息,然后通过可能的拦截器、序列化器和分区器的作用,缓存消息到消息加载器(RecordAccumulator,也称为消息收集器)中,Sender线程负责从消息加载器(RecordAccumulator)中获取消息并将其发送到Kafka中。
消息加载器
消息加载器(RecordAccumulator)主要用来缓存消息以便Sender线程可以批量发送,进而减少网络传输的资源消耗以提升性能。
消息加载器(RecordAccumulator)缓存的大小可以通过生产者参数buffer.memory配置,默认值为33444432b,即32mb。
如果生产者发送消息的速度大于发送到服务器的速度,也就是RecordAccumulator缓存不够,此时kafkaproducer的send方法调用要被被阻塞,要么抛出异常,这个取决于参数max.block.ms参数,此参数的默认值为60000ms,60s。
主线程发送过来的消息会被追加到RecordAccumulator的某个双端队列中,在RecordAccumulator内部为每个分区都维护了一个双端队列,队列中的内容就是ProducerBatch,即Deque
注意!ProducerBath不是ProducerRecord,ProducerBatch中可以包含一个或多个ProducerRecord。通俗的说ProducerRecord是生产者中创建的消息,而ProducerBatch是指一个消息批次,ProducerRecord会被包含在ProdicerBatch中。较小的ProducerRecord拼凑成一个较大的PeoducerBatch,可以减少网络请求的次数提升整体的吞吐量。
消息在网络上以字节的形式传输,在发送之前需要创建一块内存区域来保存对应消息,在kafka生产端配置中,使用java.io.ByteBuffer来实现消息内存的创建和释放。频繁的创建和释放是消耗资源的,在RecordAccumulator内部还有一个BufferPool,主要用来实现ByteBuffer的复用,实现缓存的高效利用。而BufferPool只对特定大小的ByteBuffer进行管理,其他大小的ByteBuffer不会缓存进BufferPool中,我们可以通过调整batch.size参数,以便多缓存消息。
ProducerBatch大小和batch.size参数也有密切联系。当一条消息(ProducerRecord)流入RecordAccumulator时,会先寻找与消息分区相对应的双端队列(没有则新建),查看Producer中是否还可以写入这个ProducerRrcord,如果可以则写入,如果不可以则需要创建一个新的ProducerBatch。在新建ProducerBatch时评估这条消息的大小是否超过batch.size的大小,如果不超过,就以batch.size的大小创建ProducerBatch,这样在使用完这段内存区域之后,可以通过BufferPool的管理来进行复用;如果超过,就以评估大小来创建ProducerBatch,这段内存区域不会被复用,
Sender从RecordAccumulator中获取缓存的消息之后,会进一步将原本<分区,Deque
在转换成<Node,List
请求在从sender发往Kafka之前还会保存到InFlightRequest中,InFlightRequest保存对象的具体形式为Map<Nodeid,deque