KafkaStreams serde例外

时间:2017-06-07 13:58:51

标签: java apache-kafka apache-kafka-streams

我正在玩Kafka和溪流技术;我已经为KStream创建了一个自定义序列化器和反序列化器,用于接收来自给定主题的消息。

现在,问题是我正在以这种方式创建一个serde:

JsonSerializer<EventMessage> serializer = new JsonSerializer<>();
JsonDeserializer<EventMessage> deserializer = new JsonDeserializer<>(EventMessage.class);
Serde<EventMessage> messageSerde = Serdes.serdeFrom(serializer, deserializer);

Serializer实现:

public class JsonSerializer<T> implements Serializer<T> {

    private Gson gson = new Gson();

    public void configure(Map<String, ?> map, boolean b) {
    }

    @Override
    public byte[] serialize(String topic, T data) {
        return gson.toJson(data).getBytes(Charset.forName("UTF-8"));
    }

    @Override
    public void close() {

    }
}  

反序列化器实现:

public class JsonDeserializer<T> implements Deserializer<T> {

    private Gson gson = new Gson();
    private Class<T> deserializedClass;

    public JsonDeserializer() {

    }

    public JsonDeserializer(Class<T> deserializedClass) {
        this.deserializedClass = deserializedClass;
    }

    @Override
    @SuppressWarnings("unchecked")
    public void configure(Map<String, ?> map, boolean b) {
        if(deserializedClass == null) {
            deserializedClass = (Class<T>) map.get("serializedClass");
        }
    }

    @Override
    public T deserialize(String topic, byte[] data) {
        System.out.print(data);
        if(data == null){
            return null;
        }

        return gson.fromJson(new String(data),deserializedClass);
    }

    @Override
    public void close() {

    }
}

当我尝试执行代码时,收到以下错误:

  

引起:org.apache.kafka.common.KafkaException:无法实例化类org.apache.kafka.common.serialization.Serdes $ WrapperSerde是否有公共无参数构造函数?

完全转储:https://pastebin.com/WwpuXuxB

这是我尝试使用serde的方式:

KStreamBuilder builder = new KStreamBuilder();
KStream<String, EventMessage> eventsStream = builder.stream(stringSerde, messageSerde, topic);

KStream<String, EventMessage> outStream = eventsStream
            .mapValues(value -> EventMessage.build(value.type, value.timestamp));

outStream.to("output");

此外,我不完全确定我正确设置了全局设置串行器和解串器的属性:

streamsConfiguration.put(StreamsConfig.KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass());
streamsConfiguration.put(StreamsConfig.VALUE_SERDE_CLASS_CONFIG, messageSerde.getClass());

3 个答案:

答案 0 :(得分:4)

要完成Matthias的答案,我刚刚编写了一个简单的示例,说明如何在Kafka Stream App中创建自定义Serde(Serializer / Deserializer)。它可以克隆并尝试使用:https://github.com/Davidcorral94/Kafka-Streams-Custom-Seder

首先,我创建了两个类,一个用于Serializer,另一个用于Deserializer。在这种情况下,我使用Gson library来执行序列化/反序列化。

串行

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() {

    }
}

解串器

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() {

    }
}

然后,我将它们都包装到Serde中,以便能够将它用于我的Kafka Stream应用程序。

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;
    }
}

最后,您可以将此Serde类用于下一行的Kafka Stream应用程序:

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

这实际上是使用目前可用的最新Kafka版本1.0.0!

答案 1 :(得分:3)

如果您致电Serdes.serdeFrom(...),则会获得一个用于内部使用的WrappedSerde类型(并且WrappedSerde没有非参数构造函数)。目前没有API可以调用来获取自定义Serde。相反,您需要实现自己的Serde类并包装序列化器和反序列化器#34;手动&#34;。

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;
    }
}

Properties中,您可以设置:

streamsConfiguration.put(StreamsConfig.VALUE_SERDE_CLASS_CONFIG, EventMessageSerde.class);

答案 2 :(得分:3)

另一种方法是使用StreamsBuilder代替KStreamBuilder。 {1.0}中不推荐使用KStreamBuilder。您可以在创建流时使用Consumed.with直接传递serde对象。在这种情况下,您无需创建自定义Serde类。

Serde<EventMessage> messageSerde = Serdes.serdeFrom(serializer, deserializer);

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

您可以将StringSerde保留在下面的代码中,而不是使用失败的messageSerde.getClass(),因为messageSerde只是一个没有非参数构造函数的WrappedSerde

streamsConfiguration.put(StreamsConfig.VALUE_SERDE_CLASS_CONFIG, StringSerde.class.getName());