KafkaStreams serde exception

前端 未结 3 866
野性不改
野性不改 2021-01-06 03:51

i am playing with Kafka and streams technology; i have created a custom serializer and deserializer for the KStream that i will use to receive messages from a given topic.

相关标签:
3条回答
  • 2021-01-06 04:39

    If you call Serdes.serdeFrom(...) you get a WrappedSerde type back that is for internal usage (and WrappedSerde does not have an non-argument constructor). There is currently no API you can call to get a custom Serde. Instead, you need to implement you own Serde class and wrap you serializer and deserializer "manually".

    public class EventMessageSerde implements Serde<EventMessage> {
        final private JsonSerializer<EventMessage> serializer;
        final private JsonDeserializer<EventMessage> deserializer;
    
        @Override
        public void configure(Map<String, ?> configs, boolean isKey) {
            serializer.configure(configs, isKey);
            deserializer.configure(configs, isKey);
        }
    
        @Override
        public void close() {
            serializer.close();
            deserializer.close();
        }
    
        @Override
        public Serializer<EventMessage> serializer() {
            return serializer;
        }
    
        @Override
        public Deserializer<EventMessage> deserializer() {
            return deserializer;
        }
    }
    

    In your Properties you can set:

    streamsConfiguration.put(StreamsConfig.VALUE_SERDE_CLASS_CONFIG, EventMessageSerde.class);
    
    0 讨论(0)
  • 2021-01-06 04:40

    To complete the Matthias answer I've just coded a simple example of how to create a custom Serde (Serializer / Deserializer) within a Kafka Stream App. It's is available to clone and try in: https://github.com/Davidcorral94/Kafka-Streams-Custom-Seder

    First I create two classes, one for the Serializer and another for the Deserializer. In this case I use Gson library to perform the serialization/deserialization.

    Serializer

    public class PersonSerializer implements Closeable, AutoCloseable, Serializer<Person> {
    
        private static final Charset CHARSET = Charset.forName("UTF-8");
        static private Gson gson = new Gson();
    
        @Override
        public void configure(Map<String, ?> map, boolean b) {
        }
    
        @Override
        public byte[] serialize(String s, Person person) {
            // Transform the Person object to String
            String line = gson.toJson(person);
            // Return the bytes from the String 'line'
            return line.getBytes(CHARSET);
        }
    
        @Override
        public void close() {
    
        }
    }
    

    Deserializer

    public class PersonDeserializer implements Closeable, AutoCloseable, Deserializer<Person> {
    
        private static final Charset CHARSET = Charset.forName("UTF-8");
        static private Gson gson = new Gson();
    
        @Override
        public void configure(Map<String, ?> map, boolean b) {
        }
    
        @Override
        public Person deserialize(String topic, byte[] bytes) {
            try {
                // Transform the bytes to String
                String person = new String(bytes, CHARSET);
                // Return the Person object created from the String 'person'
                return gson.fromJson(person, Person.class);
            } catch (Exception e) {
                throw new IllegalArgumentException("Error reading bytes", e);
            }
        }
    
        @Override
        public void close() {
    
        }
    }
    

    Then, I wrap both of them into a Serde to be able to use it into my Kafka Stream App.

    Serde

    public class PersonSerde implements Serde<Person> {
        private PersonSerializer serializer = new PersonSerializer();
        private PersonDeserializer deserializer = new PersonDeserializer();
    
        @Override
        public void configure(Map<String, ?> configs, boolean isKey) {
            serializer.configure(configs, isKey);
            deserializer.configure(configs, isKey);
        }
    
        @Override
        public void close() {
            serializer.close();
            deserializer.close();
        }
    
        @Override
        public Serializer<Person> serializer() {
            return serializer;
        }
    
        @Override
        public Deserializer<Person> deserializer() {
            return deserializer;
        }
    }
    

    Finally, you are able to use this Serde class into your Kafka Stream App with the next line:

    props.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, PersonSerde.class);
    

    This is actually working with the latest Kafka version available at this moment which is 1.0.0!

    0 讨论(0)
  • 2021-01-06 04:45

    Another way is using StreamsBuilder instead of KStreamBuilder. KStreamBuilder is deprecated in 1.0.0. You can directly pass serde object using Consumed.with while creating stream. You need not to create custom Serde class in this scenario.

    Serde<EventMessage> messageSerde = Serdes.serdeFrom(serializer, deserializer);
    
    StreamsBuilder builder = new StreamsBuilder();
    KStream<String, EventMessage> eventsStream = builder.stream(topic, Consumed.with(Serdes.String(), messageSerde));
    

    You can keep StringSerde in below code instead of using messageSerde.getClass() which is failing because messageSerde is just a WrappedSerde that does not have non-argument constructor.

    streamsConfiguration.put(StreamsConfig.VALUE_SERDE_CLASS_CONFIG, StringSerde.class.getName());
    
    0 讨论(0)
提交回复
热议问题