Kafka生产者——发送原理分析

整体架构

生产过程由两个线程协调运行,分别为主线程和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 。消息写入缓存时,追加到双端队列的尾部,sender读取消息的时候,从双端队列的头部读取。
注意!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 >的形式,其中Node表示broker结点,对于网络连接来说,生产者客户端是与具体的broker结点建立的链接,也就是向具体的broker结点发送消息,并不关心消息属于哪个分区;而对于KafkaProducer的应用逻辑来说,我们只关注向哪个分区发送那些消息,所以这里是一个应用逻辑层的网络IO层面的转换。

在转换成<Node,List >的形式之后,sender还会进一步封装成<Node,Request>的形式,这样就可以将Requst的请求发各个Node了,这里的Request实施Kafka的各种协议请求,对于消息发送而言是指具体的ProducerRequest。

请求在从sender发往Kafka之前还会保存到InFlightRequest中,InFlightRequest保存对象的具体形式为Map<Nodeid,deque >,它的主要作用是缓存了已经发出去但还没有收到响应的请求。InFlightRequest还提供了很多管理类的方法,通过参数配置可以限制每个链接(也就是客户端和Node之间 的链接)最多缓存的请求数,参数为:max.in.flight.request.per.com=nnection,默认值为5,即每个连接最多只能缓存5个未响应的请求,超过这个数值之后就不能再向这个连接发送更多的请求了,除非有缓存的请求收到了响应。

猜你喜欢

转载自www.cnblogs.com/luckyhui28/p/12001673.html
今日推荐