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()阻塞等待
- 加油呀,小宝们,你们都很棒呀*>*