【问题标题】:NPE while deserializing avro messages in kafka streamsNPE同时反序列化kafka流中的avro消息
【发布时间】:2019-04-02 13:14:36
【问题描述】:

我写了一个java小类来测试Avro编码的Kafka主题的消费。

    Properties appProps = new Properties();

    appProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "http://***kfk14bro1.lc:9092");
    appProps.put(AbstractKafkaAvroSerDeConfig.SCHEMA_REGISTRY_URL_CONFIG, "http://***kfk14str1.lc:8081");
    appProps.put(StreamsConfig.APPLICATION_ID_CONFIG, "consumer");
    appProps.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest");
    appProps.put(StreamsConfig.DEFAULT_DESERIALIZATION_EXCEPTION_HANDLER_CLASS_CONFIG,LogAndContinueExceptionHandler.class);


    StreamsBuilder streamsBuilder = new StreamsBuilder();

    streamsBuilder.stream(
                  "coordinates", Consumed.with(Serdes.String(), new GenericAvroSerde()))
              .peek((key, value) -> System.out.println("key=" + key + ", value=" + value));

    new KafkaStreams(streamsBuilder.build(), appProps).start();

当我运行这个类时,SerdeConfigs 正在被记录,可以在下面的日志中看到:

[consumer-56b0e0ca-d336-45cc-b388-46a68dbfab8b-StreamThread-1] INFO io.confluent.kafka.serializers.KafkaAvroSerializerConfig - KafkaAvroSerializerConfig values: 
    schema.registry.url = [http://***kfk14str1.lc:8081]
    basic.auth.user.info = [hidden]
    auto.register.schemas = true
    max.schemas.per.subject = 1000
    basic.auth.credentials.source = URL
    schema.registry.basic.auth.user.info = [hidden]
    value.subject.name.strategy = class io.confluent.kafka.serializers.subject.TopicNameStrategy
    key.subject.name.strategy = class io.confluent.kafka.serializers.subject.TopicNameStrategy

[normal-consumer-56b0e0ca-d336-45cc-b388-46a68dbfab8b-StreamThread-1] INFO io.confluent.kafka.serializers.KafkaAvroDeserializerConfig - KafkaAvroDeserializerConfig values: 
    schema.registry.url = [http://***kfk14str1.lc:8081]
    basic.auth.user.info = [hidden]
    auto.register.schemas = true
    max.schemas.per.subject = 1000
    basic.auth.credentials.source = URL
    schema.registry.basic.auth.user.info = [hidden]
    specific.avro.reader = false
    value.subject.name.strategy = class io.confluent.kafka.serializers.subject.TopicNameStrategy
    key.subject.name.strategy = class io.confluent.kafka.serializers.subject.TopicNameStrategy

但是消息没有被消费并为每条消息生成以下日志:

[normal-consumer-56b0e0ca-d336-45cc-b388-46a68dbfab8b-StreamThread-1] WARN org.apache.kafka.streams.errors.LogAndContinueExceptionHandler - Exception caught during Deserialization, taskId: 0_0, topic: coordinates, partition: 0, offset: 782205986
org.apache.kafka.common.errors.SerializationException: Error deserializing Avro message for id 83
Caused by: java.lang.NullPointerException
    at io.confluent.kafka.serializers.AbstractKafkaAvroDeserializer.deserialize(AbstractKafkaAvroDeserializer.java:116)
at io.confluent.kafka.serializers.AbstractKafkaAvroDeserializer.deserialize(AbstractKafkaAvroDeserializer.java:88)
at io.confluent.kafka.serializers.KafkaAvroDeserializer.deserialize(KafkaAvroDeserializer.java:55)
at io.confluent.kafka.streams.serdes.avro.GenericAvroDeserializer.deserialize(GenericAvroDeserializer.java:63)
at io.confluent.kafka.streams.serdes.avro.GenericAvroDeserializer.deserialize(GenericAvroDeserializer.java:39)
at org.apache.kafka.common.serialization.Deserializer.deserialize(Deserializer.java:58)
at org.apache.kafka.streams.processor.internals.SourceNode.deserializeValue(SourceNode.java:60)

但是我可以从 avro 控制台消费者那里很好地阅读,所以我知道写入主题的数据没有任何问题。下面的命令可以打印日志:

~/kafka/confluent-5.1.2/bin/kafka-avro-console-consumer --bootstrap-server http://***kfk14bro1.lc:9092 --topic coordinates --property schema.registry.url=http://***kfk14str1.lc:8081 --property auto.offset.reset=latest

【问题讨论】:

    标签: nullpointerexception apache-kafka avro confluent-schema-registry


    【解决方案1】:

    当您自己实例化 Avro Serde 时,它​​不会自动使用架构注册表 URL 进行配置。

    所以要么你必须自己配置它,要么你通过添加来定义默认 serdes:

    appProps.setProperty(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass().getName());
    appProps.setProperty(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, GenericAvroSerde.class.getName());
    

    并通过删除

    Consumed.with(Serdes.String(), new GenericAvroSerde())
    

    要配置 Serde,请使用以下代码(根据您的情况调整):

    GenericAvroSerde genericAvroSerde = new GenericAvroSerde();
    boolean isKeySerde = false;
    genericAvroSerde.configure(
         Collections.singletonMap(
             AbstractKafkaAvroSerDeConfig.SCHEMA_REGISTRY_URL_CONFIG,
             "http://confluent-schema-registry-server:8081/"),
         isKeySerde);
    

    【讨论】:

    • Consumed.with 仍被推荐,但您只需提取和预配置 serde,而不是将其仅提供 new GenericAvroSerde() 作为参数
    猜你喜欢
    • 2019-07-12
    • 2018-08-11
    • 1970-01-01
    • 2020-12-13
    • 2020-06-26
    • 1970-01-01
    • 2019-08-05
    • 2019-08-03
    • 2019-11-17
    相关资源
    最近更新 更多