kafka自定义序列化类和反序列化类

在kafka中,broker希望收到的消息的key和value都是字节数组,所以在创建生产者对象的时候必须指定序列化器。将消息进行序列化才可以进行网络传输,在kafka中默认提供了ByteArraySerializer(字节数组序列化器)、StringSerializer(字符串序列化器)、IntegerSerializer(整形序列化器)。如果发送到Kafka的消息不是提供的这几种类型,那么可以使用序列化框架来创建消息记录,如Avro、Thrift、Protobuf,或者使用自定义序列化器。但是如果使用自定义序列化器,可能在开发的过程中会有需求的变动,比如实体类增加一个字段,那么所有用到这个实体类的代码都需要修改,会出现新旧消息的兼容性问题因此不建议使用自定义的序列化器。但是在这里我们自己实现以下自定义的序列化器有助于我们理解序列化器的工作原理。

 

下面就来实现一下自定义序列化器。

1.创建项目,编写pom文件:

<dependencies>
    <dependency>
        <groupId>org.apache.kafka</groupId>
        <artifactId>kafka-clients</artifactId>
        <version>2.1.0</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.9.7</version>
    </dependency>
    <dependency>
        <groupId>org.apache.kafka</groupId>
        <artifactId>kafka_2.12</artifactId>
        <version>2.1.0</version>
    </dependency>
</dependencies>

2.编写一个实体类作为我们要发送的数据类型:

​
package demo;
public class Student{
    private String name;
    private int age;
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name=name;
    }
    public int getAge(){
        return age;
    }
    public void setAge(int age){
        this.age=age;
    }
    public Student(String name,int age){
        super();
        this.name=name;
        this.age=age;
    }
    @Override
    public String toString(){
        return "Student [name=" + name + ",age=" + age+ "]";
    }
}

​

3.定义序列化类:

package demo;
import java.nio.ByteBuffer;
import java.util.Map;
import org.apache.kafka.common.errors.SerializationException;
import org.apache.kafka.common.serialization.Serializer;
public class MySerializer implements Serializer<Student>{
    
    @Override
    public void configure(Map<String, ?> configs, boolean isKey){
        //不做任何配置
    }

    @Override
    public byte[] serialize(String topic, Student data){
    
        try{
            byte[] serializedName;//将name属性变成字节数组
            int serializedLength;//表示name属性的值的长度
            if(data == null){
                return null;
            }else{
                if(data.getName() != null){
                    serializedName = data.getName();.getBytes("UTF-8");
                    serializedLength = serializedName.length;
                }else{
                    serializedName = new byte[0];
                    serializedLength = 0;
            
                }
            }
            //我们要定义一个buffer用来存储age的值,age是int类型,需要4个字节。还要存储name的值的长度(后面会分析为什么要存name的值的长度),用int表示就够了,也是4个字节,name的值是字符串,长度就有值来决定,所以我们要创建的buffer的大小就是这些字节数加在一起:4+4+name的值的长度
            ByteBuffer buffer = ByteBuffer.allocate(4+4+serializedLength);
            //put()执行完之后,buffer中的position会指向当前存入元素的下一个位置
            buffer.putInt(data.getAge());
            //由于在读取buffer中的name时需要定义一个字节数组来存储读取出来的数据,但是定义的这个数组的长度无法得到,所以只能在存name的时候也把name的长度存到buffer中
            buffer.putInt(serializedLength);
            buffer.put(serializedName);
            return buffer.array();
            

        }catch(Exception e){
            throw new SerializationException("error when serializing..."+e);
        }
    }

    @Override
    public void close(){
        //不需要关闭任何东西
    }


}

4.自定义反序列化类:

package demo;
import java.nio.ByteBuffer;
import java.util.Map;
import org.apache.kafka.common.errors.SerializationException;
import org.apache.kafka.common.serialization.Deserializer;

public class MyDeserializer implements Deserializer<Student>{

    @Override
    public void configure(Map<String, ?> configs, boolean isKey){
        
    }

    @Override
    public Student deserialize(String topic, byte[] data){
        int age;
        int nameLength;
        String name;
        try{
            if(data == null){
                return null;
            }
            if(data.length < 8){
                throw new SerializationException("Size of data received by IntegerDeserializer is shorter than expected...");
            }
            ByteBuffer buffer = ByteBuffer.wrap(data);//wrap可以把字节数组包装成缓冲区ByteBuffer
            //get()从buffer中取出数据,每次取完之后position指向当前取出的元素的下一位,可以理解为按顺序依次读取
            age = buffer.getInt();
            nameLength = buffer.getInt();
            /*
             * 定义一个字节数组来存储字节数组类型的name,因为在读取字节数组数据的时候需要定义一个与读取的数组长度一致的数组,要想知道每个name的值的长度,就需要把这个长度存到buffer中,这样在读取的时候可以得到数组的长度方便定义数组
             */
            byte[] nameBytes = new byte[nameLength];
            buffer.get(nameBytes);
            name = new String(nameBytes, "UTF-8");
            return new Student(name, age);
        }catch(Exception e){
            throw new SerializationException("error when deserializing..."+e);
        }


    }

    @Override
    public void close(){
        
    }
}

5.创建生产者:

​
package demo;
import java.util.Properties;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.Producer;
import org.apache.kafka.clients.producer.ProducerRecord;

public class MyProducer{
    
    public static void main(String[] args){
        
        Properties props = new Properties();
        props.put("bootstrap.servers", "192.168.184.128:9092");
        props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        //设置value的序列化类为自定义序列化类全路径
        props.put("value.serializer", "demo.MySerializer");
        Producer<String, Student> producer = new KafkaProducer<String, Student>(props);
        //定义一个要发送的Student类型的value
        Student s = new Student("Bob", 14);
        Student s2 = new Student("Sam", 17);
        //参数一为topic,参数二为value
        ProducerRecord<String, Student> record = new ProducerRecord<String, Student>("WYH-serializer-topic", s);
        ProducerRecord<String, Student> record1 = new ProducerRecord<String, Student>("WYH-serializer-topic", s2);
        producer.send(record);
        producer.send(record1);
        producer.close();
    }
}

​

6.创建消费者:

package demo;
import java.util.Collections;
import java.util.Properties;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.ConsumerRecord;

public class MyConsumer{
    
    public static void main(String[] args){
        
        Properties props = new Properties();
        props.put("bootstrap.servers", "192.168.184.128:9092");
        props.put("group.id", "WYH-deserializer-app");
        props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        //设置value的反序列化类为自定义序列化类全路径
        props.put("value.deserializer", "demo.MyDeserializer");
        props.put("auto.offset.reset", "earliest");
        KafkaConsumer<String, Student> consumer = new KafkaConsumer<String, Student>(props);
        consumer.subscribe(Collections.singletonList("WYH-serializer-topic"));
        while(true){
            CosumerRecords<String, Student> records = consumer.poll(100);
            for(ConsumerRecord<String, Student> consumerRecord: records){
                Student student = consumerRecord.value();
                System.out.println(student.toString());
            }
        }
    }
}

​

7.启动消费者,再启动生产者,查看控制台打印信息:

这样就实现了对实体类的数据类型的序列化。

Spring Kafka 提供了方便的方式来创建和管理自定义Kafka 生产者和消费者。它允许你通过实现 `KafkaProducerFactory` 和 `KafkaConsumerFactory` 接口来自定义生产和消费的行为。 对于自定义生产者工厂 (`KafkaProducerFactory`): ```java @Configuration public class CustomProducerConfig { @Bean public KafkaProducerFactory<String, String> producerFactory() { Map<String, Object> configProps = new HashMap<>(); // 自定义配置,例如设置acks、retries等 configProps.put(ProducerConfig.ACKS_CONFIG, "all"); configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, YourCustomKeySerializer.class); configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, YourCustomValueSerializer.class); return new DefaultKafkaProducerFactory<>(configProps, createProducerConfig()); } private ProducerConfig createProducerConfig() { return new ProducerConfig(); } } ``` 这里的 `YourCustomKeySerializer` 和 `YourCustomValueSerializer` 是你自定义序列化器实现。 对于自定义消费者工厂 (`KafkaConsumerFactory`): ```java @Configuration public class CustomConsumerConfig { @Bean public ConcurrentKafkaConsumerFactory<String, String> consumerFactory() { Map<String, Object> configProps = new HashMap<>(); // 自定义配置,例如设置bootstrap servers、group id等 configProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); configProps.put(ConsumerConfig.GROUP_ID_CONFIG, "your-consumer-group"); return new ConcurrentKafkaConsumerFactory<>(createConsumerConfig(), keyDeserializerClass(), valueDeserializerClass()); } private ConsumerConfig createConsumerConfig() { return new ConsumerConfig(); } // 这里分别定义自定义的key和value反序列化器 private Class<? extends Deserializer<String>> keyDeserializerClass() { return YourCustomKeyDeserializer.class; } private Class<? extends Deserializer<String>> valueDeserializerClass() { return YourCustomValueDeserializer.class; } } ``` 这样,Spring Kafka会根据你的工厂配置来实例化对应的生产者和消费者。记得提供完整的路径,并确保它们实现了Kafka序列化反序列化接口。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

QYHuiiQ

听说打赏的人工资翻倍~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值