首先,我们先上图。
概念解释:
生产者拦截器 ProducerInterceptor
拦截器(Interceptor)功能最早在kafka0.10.0.0中引入,kafka一共有两种拦截器:生产者拦截器和消费者拦截器。
生产者拦截器可以用来在消息发送前做一些处理,例如按照某个规则过滤不符合要求的消息、修改消息的内容等,也可以用来在发送回调逻辑前做一些定制化的需求,比如统计类工作。
要实现自定义拦截器,需要实现ProducerInterceptor接口。
序列化(Serializer)
生产者需要使用序列化(Serializer)将对象转换成字节数组才能够通过网络发送给kafka。而在消费者端需要使用反序列化(Deserializer)把从kafka接收到的字节数组转换成相应的对象。为了方便,消息的key和value都要序列化。常见的序列化方式:ByteArraySerializer、ByteBufferSerializer、BytesSerializer、Long(Double Integer String)Serializer。要自定义序列化的话需要实现Serializer接口。
分区器(Partitioner)
分区选择器,默认是murmur2 对于key进行hash计算然后对于总分区数求模以此得到被发送的分区号,当然我们实现producer时可以自定义partition,或者指定特定分区。分区器的作用就是为消息分配消息。
单个分区的消息是有序的。
消息累加器 (RecordAccumulator)也称消息收集器
主线程调用send()方法将已经和topic和分区绑定好的消息,以分区为单位维护一个双端队列,将消息缓存起来。当达到一定的条件,会唤醒Sender线程发送RecordAccumulator里面的消息。RecordAccumulator缓存的大小可以通过生产者客服端参数buffer.memory配置,默认值为33554432B,即32M。如果生产者发送消息的速度超过发送到服务器的速度,则会导致空间不足。这个时候KafkaProducer的send()方法调用就会阻塞,要么抛出异常,这个取决于参数max.block.ms的配置。此参数默认值为60000,即60s。
RecordAccumulator由对多个双端队列构成,主线程发送的消息都会追加到其中的某个双端队列。其内部会为每个分区都为维护一个双端队列(Deque)中,队列中的内容即Deque。消息在写入缓存时,追加至双端队列的尾部;队列中的ProducerBatch是由多个ProducerRecord消息组成。
kafka生产者架构图,步骤。
1、主线程KafkaProducer创建消息,然后通过生产者拦截器
2、生产者拦截器对消息的key ,value做一定的处理,交给序列化器,
3、序列化器对消息key和vlue做序列化处理,然后给分区器
4、分区器给消息分配分区、并发送给消息收集器
5、一条ProducerRecord,添加到RecordAccumulator,首先会根据分区确定对应分区所在的双端队列,在双端队列获取尾部的一个ProducerBatch对象,查看该ProducerBatch是否可以写入该ProducerRecord消息,如果可以则写入,不能的话,会在双端队列末尾在创建一个ProducerBatch对象,创建时会评估这条消息是否是否超过batch.size参数的大小。如果不超过,就以batch.size参数的大小来创建ProducerBatch对象。通过sender线程发送消息
6、sender线程获取RecordAccumulator中的消息,需要将原本的<分区,Deque>形式再次封装成<Node,List的形式,其中Node节点表示Kafka集群中的broker节点。对于网络连接而言,生产者客户端是与具体broker节点建立连接,也就是向具体的broker节点发送消息。而并不关心消息属于哪个分区;sender线程还会进一步封装成<Node,Request>的形式,这样就可以将请求发往各个Node了。这里的Request是指kafka的各种请求协议。
7、sender线程发往Kafka之前还会保存到InFlightRequest中,InFlightRequest保存对象的具体形式为Map<NodeId,Deque>,他的主要作用是缓存了已经发出去但是还没有收到相应的请求。
8、sender将Request交给Selector准备发送。
9、Selector将Request发送到对应的kafka节点(Broker)。
10、Selector相应结果反馈给InFlightRequest。
11、主线程清理RecordAccumulator已经发送完毕的消息。