Kafka生产者提交数据流程
在平时的工作中,经常需要引入消息队列中间件,Kafka作为目前主流的消息队列,是程序员必不可少的技能。
本文结合kafka-clients源码回顾一下Kafka生产者提交数据的流程。kafka-clients版本为3.3.1,Java版本kafka-clients的maven路径:
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>3.3.1</version>
</dependency>
一、Kafka生产者提交数据整体流程
按照先整体再局部的方法,先复习一下Kafka生产者提交数据的整体流程,然后再从整体出发,剖析各个部分的细节。
- Kafka发送之前需要创建KafkaProducer,调用send方法提交数据。
public class KafkaSendDemo {
public static void main(String[] args) {
Properties properties = new Properties();
// 设置kafka集群地址
properties.setProperty(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"");
// 设置key/value序列化方法
properties.setProperty(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
properties.setProperty(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
// 创建生产者
KafkaProducer<String, String> producer = new KafkaProducer<>(properties);
// 发送数据
Future<RecordMetadata> send = producer.send(new ProducerRecord<>("topic1", 1, "hello", "hello"));
}
}
Kafka支持同步提交和异步提交:同步提交调用send(ProducerRecord)方法; 异步提交调用send(ProducerRecord, Callback)方法。
- 调用send方法提交数据后,会接入拦截器,在拦截器中可以对数据进行过滤、转换等操作。通过实现 ProducerInterceptor<K, V>接口,并实现onSend方法可以自定义拦截器。随后在创建KafkaProducer时设置配置项interceptor.classes的值指定拦截器。
public class MyProducerInterceptor implements ProducerInterceptor<String, String> {
@Override
public ProducerRecord<String, String> onSend(ProducerRecord<String, String> record) {
return null;
}
@Override
public void onAcknowledgement(RecordMetadata metadata, Exception exception) {
}
@Override
public void close() {
}
@Override
public void configure(Map<String, ?> configs) {
}
}
-
通过拦截器的数据会进入序列化器,在序列化器中对数据的key/value进行序列化操作。
-
序列化过后,Kafka会对数据进行分区,确定数据将写入topic中的哪个分区。确认分区的逻辑流程为:
- 第一步,判断是否直接指定了分区,如果指定了分区直接退出;
- 第二步,未指定分区的数据,判断是否指定了分区器,如果指定了分区器,则使用指定的分区策略设置分区。
- 第三步,未指定分区且未设置分区器的数据,判断是否有key且key不应该被忽略,如果满足,则分区为:key哈希值对topic分区数量取模;否则返回-1,表示可以使用任意分区,后续使用内部逻辑确定分区(一般为随机分区)。
-
确认分区后,将数据放入累积器中,累积器的作用为:设置一个内存阈值和时间阈值,当数据累积到一定大小或到达时间节点,则向服务端传输数据,可以提高数据传输效率,内存阈值初始值为16KB。
-
通过Sender将满足条件的数据发送到服务端。
二、从源码中体现整个流程
1.发送数据,最终都是调用send(ProducerRecord<K, V> record, Callback callback)方法。
send(ProducerRecord<K, V> record, Callback callback)方法:
2.执行拦截器,在send(ProducerRecord<K, V> record, Callback callback)方法中,首先调用this.interceptors.onSend(record)方法,对已注册的拦截器进行一一调用。
interceptors类型为ProducerInterceptors<K, V>,改变量初始化逻辑为:如果在创建KafkaProducer是传入interceptors,则直接使用传入的interceptors;否则
从配置中获取 ProducerConfig.INTERCEPTOR_CLASSES_CONFIG配置值,创建interceptors。
this.interceptors.onSend(record)方法,遍历interceptors中的每个interceptor,每个interceptor执行onSend(record)方法,最后返回经过拦截器处理的数据。
3.序列化器
在执行序列化器之前,会先进行一些其他操作,如:
- 将数据封装成AppendCallbacks对象,主要作用是当数据发送完成后调用拦截器的onAcknowledgement()方法;记录数据在topic中的分区号。
- waitOnMetadata()方法检查更新topic信息,检查生产者是否关闭。
对key/value 进行序列化操作:
4.分区器
调用partition(ProducerRecord<K, V> record, byte[] serializedKey, byte[] serializedValue, Cluster cluster)计算数据的分区。