生产者客户端有两个线程协调运行,分别是 主线程 和 Sender线程 。主线程负责创建消息、序列化消息以及为消息分区等工作,由KafkaProducer、拦截器、序列化器、分区器、消息累加器组成;Sender负责发送消息,由Sender、InFlightRequest、Selector。
1. KafkaProducer
//设置客户端参数
Properties properties = new Properties();
properties.put(“key.serializer”,“org.apache.kafka.common.serialization.StringSerializer”);
properties.put(“value .serializer”, “org.apache.kafka.common.serialization.StringSerializer”);
properties.put(“bootstrap.servers”,brokerList);
//创建KafkaProducer实例
KafkaProducer<String,String> producer = new KafkaProducer<>(properties);
//构建所需妥发送的消息
ProducerRecord<String , String> record = new ProducerRecord<>(topic,“hello, Kafka!”);
//发送消息
producer.send(record) ;
2. 拦截器(非必须)
生产者拦截器既可以用来在消息发送前做一些准备工作,比如按照某个规则过滤不符合要求的消息、修改消息的内容等,也可以用来在发送回调逻辑前做一些定制化的需求,比如统计类工作。
3. 序列化器
生产者需要用序列化器(Serializer)把对象转换成字节数组才能通过网络发送给 Kafka 。而在对侧,消费者需要用反序列化器(Deserializer)把从 Kafka 中收到的字节数组转换成相应的对象。
4. 分区器
消息经过序列化之后就需要确定它发往的分区,如果消息 ProducerRecord(下文简称 Record)中指定了partition字段,那么就不需要分区器的作用,因为 partition 代表的就是所要发往的分区号。如果消息 Record 中没有指定 partition 字段,那么就需要依赖分区器, 根据key这个字段来计算 partition 的值。分区器的作用就是为消息分配分区。
5. 消息累加器
累加器用来缓存消息以便 Sender线程批量发送。缓存大小通过客户端参数 buffer.merory 配置,默认32M。
- ProducerBatch(下文简称Batch)
Batch 是一批 Record,包含至少一个 Record,这样可以使字节更紧凑,从而达到减少请求次数和提高网络吞吐量的目的。 - 消息写入累加器的过程:
2.1 根据 Record 的 partition 找到分区对应的双端队列(没有就创建),从中取出队尾 Batch(没有就创建);
2.2 检查 Batch 中是否有足够空间写入该 Record,如果可以则写入,流程结束;
2.3 如果空间不够,则判断 Record 的大小是否超过 batch.size(默认16k),如果不超过则通过客户端内存管理获得内存创建 Batch,否则申请相应大小的内存创建 Batch;
2.4 将 record 写入; - 客户端内存管理:
为了优化内存分配,客户端实现 BufferPool 进行内存管理,BufferPool 会根据 batch.size 的大小动态创建内存块,避免内存的频繁创建和释放。
6. Sender
Sender 从累加器中获取缓存的消息之后,会进一步将原本 <分区,Deque< ProducerBatch>> 的保存形式转变成 <Node, List< ProducerBatch> 的形式,其中 Node 表示 Kafka 集群的 broker 节点。
在转换成 <Node,List< ProducerBatch>> 的形式之后,Sender 还会进一步封装成 <Node,Request> 的形式,这样就可以将 Request 请求发往各个Node了,这里的 Request 是指Kafka的各种协议请求,对于消息发送而言就是指具体的 ProduceRequest 。1
7. InFlightRequest
InFlightRequests保存对象的具体形式为 Map<Nodeld,Deque< Request>>,它的主要作用是缓存了已经发出去但还没有收到响应的请求(Nodeld是一个String类型,表示节点的id编号)。通过 Deque< Request>的 size,可以判断对应的Node节点是否过载,从而限制向其发送消息,达到负载均衡的目的;除此之外,还可据此得出负载最小的 Node,元数据的更新请求便可发到该Node,完成元数据更新。
元数据是指Kafka集群的元数据,这些元数据具体记录了集群中有哪些主题,这些主题有哪些分区,每个分区的leader副本分配在哪个节点上,follower副本分配在哪些节点上,哪些副本在AR、ISR等集合中,集群中有哪些节点,控制器节点又是哪一个等信息。
元数据的更新是在客户端内部进行的,由Sender线程在 1)客户端找不到对应的元数据信息(如找不到topic)或 2)超过 metadata.max.ago.ms 时间没更新(默认5分钟)时发起。
8. Selector
处理网络连接与读写处理。2
详见 – 【深入Kafka - 服务端】 ↩︎