KafkaStreams:处理KStream-KTable连接中的反序列化异常

时间:2020-06-06 07:02:29

标签: apache-kafka-streams

假设我们正在KStream和KTable之间进行内部联接,如下所示:

        StreamsBuilder sb = new StreamsBuilder();
        JsonSerde<SensorMetaData> sensorMetaDataJsonSerde = new JsonSerde<>(SensorMetaData.class);
        KTable<String, String> kTable = sb.stream("sensorMetadata",
                Consumed.with(Serdes.String(), sensorMetaDataJsonSerde)).toTable();

        KStream<String, String> kStream = sb.stream("sensorValues",
                Consumed.with(Serdes.String(), Serdes.String()));

        KStream<String, String> joined = kStream.join(kTable, (left, right)->{return getJoinedOutput(left, right);});

有关该应用程序的几点要点:

  1. SensorMetaData是一个POJO

        public class SensorMetaData{        
        String sensorId;
        String sensorMetadata;      
        }
    
  2. DEFAULT_DESERIALIZATION_EXCEPTION_HANDLER_CLASS_CONFIG设置为org.apache.kafka.streams.errors.LogAndContinueExceptionHandler

  3. 如果反序列化失败,JsonSerde类将抛出SerializationException。

当我运行该应用程序并向这两个主题发送消息时,联接将按预期工作。

现在,我如下更改了SensorMetaData的架构,并在新节点上重新部署了应用程序

public class SensorMetaData{            
String sensorId;    
MetadataTag[] metadataTags;         
}

应用程序启动后,当iam向sensorValues主题(streams主题)发送消息时,该应用程序将因org.apache.kafka.common.errors.SerializationException关闭。查看堆栈跟踪,我发现由于SensorMetaData中的架构更改,执行连接时无法反序列化SensorMetaData。反序列化方法中的断点显示,它试图对主题“ app-KSTREAM-TOTABLE-STATE-STORE-0000000002-changelog”中的数据进行反序列化。

所以问题是,为什么即使DEFAULT_DESERIALIZATION_EXCEPTION_HANDLER_CLASS_CONFIG设置为org.apache.kafka.streams.errors.LogAndContinueExceptionHandler,应用程序仍要关闭而不是跳过不良记录(即具有旧模式的记录)?

但是,当应用程序在读取主题“ sensorMetadata”(即sb.stream(“ sensorMetadata”))时遇到不良记录时,它会成功跳过记录,并警告“由于反序列化错误而跳过了记录”。

为什么join在这里不跳过不良记录?如何处理这种情况。我希望应用程序跳过记录并继续运行而不是关闭。这是堆栈跟踪

at kafkastream.JsonSerde$2.deserialize(JsonSerde.java:51)
    at org.apache.kafka.streams.state.internals.ValueAndTimestampDeserializer.deserialize(ValueAndTimestampDeserializer.java:54)
    at org.apache.kafka.streams.state.internals.ValueAndTimestampDeserializer.deserialize(ValueAndTimestampDeserializer.java:27)
    at org.apache.kafka.streams.state.StateSerdes.valueFrom(StateSerdes.java:160)
    at org.apache.kafka.streams.state.internals.MeteredKeyValueStore.outerValue(MeteredKeyValueStore.java:207)
    at org.apache.kafka.streams.state.internals.MeteredKeyValueStore.lambda$get$2(MeteredKeyValueStore.java:133)
    at org.apache.kafka.streams.processor.internals.metrics.StreamsMetricsImpl.maybeMeasureLatency(StreamsMetricsImpl.java:821)
    at org.apache.kafka.streams.state.internals.MeteredKeyValueStore.get(MeteredKeyValueStore.java:133)
    at org.apache.kafka.streams.processor.internals.ProcessorContextImpl$KeyValueStoreReadWriteDecorator.get(ProcessorContextImpl.java:465)
    at org.apache.kafka.streams.kstream.internals.KTableSourceValueGetterSupplier$KTableSourceValueGetter.get(KTableSourceValueGetterSupplier.java:49)
    at org.apache.kafka.streams.kstream.internals.KStreamKTableJoinProcessor.process(KStreamKTableJoinProcessor.java:77)
    at org.apache.kafka.streams.processor.internals.ProcessorNode.lambda$process$2(ProcessorNode.java:142)
    at org.apache.kafka.streams.processor.internals.metrics.StreamsMetricsImpl.maybeMeasureLatency(StreamsMetricsImpl.java:806)
    at org.apache.kafka.streams.processor.internals.ProcessorNode.process(ProcessorNode.java:142)
    at org.apache.kafka.streams.processor.internals.ProcessorContextImpl.forward(ProcessorContextImpl.java:201)
    at org.apache.kafka.streams.processor.internals.ProcessorContextImpl.forward(ProcessorContextImpl.java:180)
    at org.apache.kafka.streams.processor.internals.ProcessorContextImpl.forward(ProcessorContextImpl.java:133)
    at org.apache.kafka.streams.processor.internals.SourceNode.process(SourceNode.java:101)
    at org.apache.kafka.streams.processor.internals.StreamTask.lambda$process$3(StreamTask.java:383)
    at org.apache.kafka.streams.processor.internals.metrics.StreamsMetricsImpl.maybeMeasureLatency(StreamsMetricsImpl.java:806)
    at org.apache.kafka.streams.processor.internals.StreamTask.process(StreamTask.java:383)
    at org.apache.kafka.streams.processor.internals.AssignedStreamsTasks.process(AssignedStreamsTasks.java:475)
    at org.apache.kafka.streams.processor.internals.TaskManager.process(TaskManager.java:550)
    at org.apache.kafka.streams.processor.internals.StreamThread.runOnce(StreamThread.java:802)
    at org.apache.kafka.streams.processor.internals.StreamThread.runLoop(StreamThread.java:697)
    at org.apache.kafka.streams.processor.internals.StreamThread.run(StreamThread.java:670)
INFO stream-client [app-814c1c5b-a899-4cbf-8d85-2ed6eba81ccb] State transition from ERROR to PENDING_SHUTDOWN 

1 个答案:

答案 0 :(得分:1)

Kafka在读取RocksDB文件时未使用DEFAULT_DESERIALIZATION_EXCEPTION_HANDLER_CLASS_CONFIG中的处理程序(请参阅stacktrace提及类StateSerdes)。这就是为什么它对于源主题中的记录效果很好,但是在对表中的数据进行反序列化时会失败。

我对Kafka并没有超级的经验,但是我不断地听到一遍又一遍的话:如果有什么变化,请将具有新格式的数据复制到另一个主题或删除数据,重置偏移量并重新处理。

在这种情况下,最好删除KTable文件(用于ktable的内部主题),然后让应用使用新结构重新生成KTable。

这个几个月前的博客进一步解释了该过程或删除数据:https://www.confluent.io/blog/data-reprocessing-with-kafka-streams-resetting-a-streams-application/

分享一些见识:卡夫卡是非常复杂的野兽。为了在生产中成功管理它,您需要构建大量的工具,进行代码维护,并(通常)更改部署过程以适合Kafka。

相关问题