使用SpecificAvroSerde读取Kafka事件时报Avro反序列化异常如何解决?
异常引发原因
该异常本质是Avro SpecificRecord反序列化时无法匹配写入端(Writer)Schema中指定的类型,核心触发逻辑如下:
'org.apache.kafka.common.errors.SerializationException: Error deserializing Avro message for id 4494 Caused by: org.apache.kafka.common.errors.SerializationException: Could not find class topLevelRecord specified in writer's schema whilst finding reader's schema for a SpecificRecord.'
- 上游生产端序列化消息时使用的Schema根类型名称为
topLevelRecord,和你本地用于反序列化的VertexTransactionRawEventValue类对应的Schema根类型名称、命名空间不匹配。SpecificRecord反序列化要求Writer Schema指定的类全限定名和本地存在的SpecificRecord实现类完全一致,找不到对应类就会抛出该异常。 GenericRecord可以正常读取的原因是它不需要绑定具体Java类,会直接从Schema Registry拉取对应id的Schema动态解析数据,不需要查找本地类。- 常见的触发场景包括:生产端直接动态构造Schema序列化(没有使用预生成的Specific类,默认根名称为
topLevelRecord)、本地生成Specific类的Schema和Schema Registry中id为4494的Schema不一致、本地类路径下缺少对应类型的实现类。
解决方案
你可以根据实际场景选择以下任意方案解决:
- 方案1:对齐上下游Schema
从Schema Registry拉取id为4494的Schema文件,使用avro-maven-plugin/gradle-avro-plugin等工具重新生成本地SpecificRecord类,替换当前的VertexTransactionRawEventValue,保证生成类的全限定名和Schema中namespace+name属性完全一致。
原有Serde配置不需要修改,保持如下即可:final Map<String, String> schemaMap = Collections.singletonMap(AbstractKafkaAvroSerDeConfig.SCHEMA_REGISTRY_URL_CONFIG, schemaServerURL); final SpecificAvroSerde<VertexTransactionRawEventValue> vertexTransactionRawEventValueAvroSerde = new SpecificAvroSerde<>(); vertexTransactionRawEventValueAvroSerde.configure(schemaMap, false); KStream<String, VertexTransactionRawEventValue> vertexTransactionRawEventValueKStream = streamsBuilder.stream(vertexTransactionRawEventTopic,Consumed.with(Serdes.String(), vertexTransactionRawEventValueAvroSerde)); - 方案2:指定Reader Schema兼容反序列化
如果无法修改本地类,且本地VertexTransactionRawEventValue的Schema和Writer Schema是向前兼容的,可以在Serde配置中指定Reader Schema,开启Avro Schema演化能力:final Map<String, Object> schemaMap = new HashMap<>(); schemaMap.put(AbstractKafkaAvroSerDeConfig.SCHEMA_REGISTRY_URL_CONFIG, schemaServerURL); schemaMap.put(KafkaAvroDeserializerConfig.READER_SCHEMA_CONFIG, VertexTransactionRawEventValue.getClassSchema()); schemaMap.put(KafkaAvroDeserializerConfig.SPECIFIC_AVRO_READER_CONFIG, true); final SpecificAvroSerde<VertexTransactionRawEventValue> vertexTransactionRawEventValueAvroSerde = new SpecificAvroSerde<>(); vertexTransactionRawEventValueAvroSerde.configure(schemaMap, false); - 方案3:手动转换GenericRecord
如果上下游Schema无法对齐,可以继续使用GenericRecord接收消息,之后手动把字段映射为VertexTransactionRawEventValue对象即可。
内容的提问来源于stack exchange,提问作者Liang Zhang
相关产品推荐
相关产品推荐

