kafka-生产者-(day-3)

Serializer和Deserializer是将对象序列化或者反序列化为[]byte,因为客户端发送的消息(key+value)都得是[]byte
  • 在主线程:将数据封装成ProducerRecord对象,调用send()将消息存在RecordAccumulator中执行完这一步的时候,要思考把这个发送到topic【这个ProducerRecord对象包指定了】下面的哪个分区,1,ProducerRecord对象[partition字段]里面写了他自己想去的分区序号;2,没写,那就需要通过Partitioner.partition()去选择一个分区了
  • 在创建kafkaproducer时传入的key/value会被保存在abstractConfig的originals字段中,abstractConfig的核心方法时getConfiguredInstance(),这个函数的作用是通过反射机制时理化original字段中指定的类
  • 设计Configurable接口的目的是统一反射后的初始化过程,对外提供统一的接口。上面那个方法通过反射构造的对象,都是通过无参构造函数构造的,而现在这个接口的configure()封装对象初始化需要一个originals字段的参数-----没太明白这里是想说明啥
  • DefaultPartitioner.partition()是在ProducerRecord没有明确指定分区对象的时候用的,分情况:1:没有key,只有value,根据counter(原子性)和partition个数取模来确定分区【counter不断增加,保证消息不会被分到用一个分区里面】;2:有key,有value,对key使用hash与分区个数进行取模

RecordAccumlator

  • 我们现在了解他里面就是缓存了很多的消息,具体怎样实现的
  • 通过map(TopicPartition,ArrayDeque)来实现,就是前者key是要发往的分区,后者是要发的消息的对列,每个RecordBatch拥有一个MemeoryRecords对象的引用,他才是最终缓存消息的地方
MemeryRecords表示的是多个消息的集合
RecordAccumlator重要字段介绍
  • buffer:用于保存数据的地方

  • writeLimit:记录buffer字段最多可写入多少字节的数据

  • compressor:将消息压缩后输出到buffer

    //compressor重要字段介绍

    • bufferStream:在buffer上建立的ByteBufferOutputStream对象,作用就是当写入消息超过了ByteBuffer容量,ByteBufferOutStream会自动进行扩容
    • DataOutPutStream:为前者添加了压缩功能【压缩方法是GZIP,SNAPPY,LZ4,由kafkaProducer的compressionType字段决定】
    • Compressor.enstimateBytesWritten()方法通过压缩率,估算因子等来估计已写入的字节,作用:判断MemoryRecords是否写满
  • writable:设置MemoryRecords模式,在他发送前,设置成只读模式

RecordAccumlator重要方法介绍
  • append():先判断MemoryRecords是否可写,调用Compressor.put*()方法,将消息写入ByteBuffer中
  • hasRoomFor():根据Compressor.enstimateBytesWritten()来估算以写入的字节,来判断是否能容纳值后要写入的数据,要是不行,ByteBuffer就要进行扩容
  • close():要是ByteBuffer出现了扩容情况,那么MemoryRecords.buffer和ByteBufferOutStream.buffer将指向不同的ByteBuffer,那么现在close()做两件事:1,就会让他们指向同一个ByteBuffer【通过使MemoryRecords.buffer指向扩容获得ByteBuffer】;2,将writable设置成false
  • sizeInBytes():对于可写的MemoryRecords,返回的是ByteBufferOutputStream.buffer字段的大小;对于可读的,返回的是MemoryRecords.buffer的大小
RecordBatch(每一个RecordBatch封装了一个MemeryRecords对象)
重要字段介绍
  • recordCount:Record的个数

  • maxRecordSize:最大Record的字节数

  • attempts:尝试发送当前RecordBatch的次数

  • lastAttemptMs:最后一次发送RecordBatch的时间

  • records:指向MemoryRecords对象

  • topicPartition:当前RecordBatch缓存消息发送的目的地

  • producerFuture:ProduceRequestResult类型,标记RecordBatch状态的Future对象

    //1
    ProducerFuture通过包含count=1的CountDownLatch实现了Future功能
    当RecordBatch中的消息被发送,会调用ProducerRequestResult.done(),将producerFuture标记为完成,,通过ProducerRequestResult.error来判断是否正常发送,然后调用CountDownLatch的down()来唤醒阻塞的await()
    //2
    ProduceRequestResult中包含重要字段baseOffset,这是第一条消息在RecordBath中的位置,根据他可以退出后面消息的位置

  • lastAppendTime:最后一次在RecordBatch追加消息的时间

  • thunks:Thunk对象的集合

  • offsetCounter:记录没个消息在RecordBatch中的偏移量

  • retry:要是RecordBatch中数据发送失败,是否继续尝试

thunks详解
  • kafkaProducer.send()的第二个参数是Callback对象,针对每个消息的回调函数,RecordBatch.thunks就是消息回调对象对列,每一个消息回调对象里面包含Callback字段以及future字段,future字段他是FutureRecordMetadata类型
  • FutureRecordMetadata类型包含两个重要字段
  • result:ProduceRequestResult类型,指向对应消息所在RecordBatch的produceFuture字段
  • relativeOffset:记录该消息在RecordBatch中的偏移量
RecordBath核心方法
  • tryAppend(),目的:尝试将消息添加到RecordBatch中缓存
  • done(),目的:将服务端返回的信息封装成RecordMetadata,调用消息对应的Callback。当生产者受到响应,调用FutureRecordMetadata.get()方法返回他生成的RecordMetadata对象。
这里future来future去的,要是我是读者,可能看的会一脸蒙蔽,没关系的,咱做就把他做好,现在来理理关系哈
  • future概念:代表未来某个时间可获取的结果的对象,通俗来说就是未来的结果存储的地方,用于异步编成,先发请求,后取结果
  • 为了能在调用send线程先返回一个结果,他就是这样了:Futurefuture=producer.send(record, callback),这样的话你有两个选择:异步:传入callback,等kafka调用;同步:调用future.get()阻塞等待结果
  • 详细步骤如下:
  • 在将消息封装成ProducerRecord对象存到RecordAccumlator的时候,同时创建在FutureRecordMetadata:某条消息的发送future,里面包含几个重要元素1:relativeOffset:在RecordBatch中的位置;2:callback:回调函数;3:resule:引用的是ProduceRequestResult
  • 在将消息封装成ProducerRecord对象存到RecordAccumlator的时候,(会进行判断,要是存不下,就在创建一个新的RecordBatch),将FutureRecordMetadata加入RecordBatch的thunks队列
  • Sender线程从RecordAccumlator拉取可以发送的RecordBatch将他聚合成一个ProduceRequest发送到broker
  • broker ack
  • 疑问:下面第一个返回的RecoedMetadata着各种不就有callback吗,为什么还要遍历thunks呢?-后面是答案
  • kafka调用ProducerRequestResult.done(),他内部调用CountDownLatch(1).countDown()唤醒所有future,所以future.get()返回RecoedMetadata对象-这个是你要是同步调用了feature.get()等待的时候有用
  • kafka遍历改RecordBatch的所有thunks,执行所有消息的callback-这个是异步返回结果
  • ProduceRequestResult:RecordBatch级别的future(所有消息共享一个)
  • CountDownLatch(1):实现了future.get()阻塞等待
  • 加油呀,小宝们,你们都很棒呀*>*