Estoy escribiendo una aplicación de flujo de Kafka en Java que toma los temas de entrada creados por un conector que utiliza el registro de esquema y Avro tanto para la llave y convertidor de valores. El conector produce los siguientes esquemas:
key-schema: "int"
value-schema:{
"type": "record",
"name": "User",
"fields": [
{"name": "firstname", "type": "string"},
{"name": "lastname", "type": "string"}
]}
En realidad, hay varios temas, la clave-esquema es siempre "int" y el valor-esquema es siempre un registro de algún tipo (usuario, producto, etc.). Mi código contiene las siguientes definiciones
Map<String, String> serdeConfig = Collections.singletonMap("schema.registry.url", schemaRegistryUrl);
Serde<User> userSerde = new SpecificAvroSerde<>();
userSerde.configure(serdeConfig, false);
Al principio traté de consumir el tema con algo así Consumed.with(Serdes.Integer(), userSerde);
, pero que no funcionó porque Serdes.Integer () espera enteros para ser codificados utilizando 4 bytes, pero utiliza Avro una longitud variable de codificación. Usando Consumed.with(Serdes.Bytes(), userSerde);
trabajaron pero realmente quería int y no por bytes por lo que he cambiado de código a este
KafkaAvroDeserializer keyDeserializer = new KafkaAvroDeserializer()
KafkaAvroSerializer keySerializer = new KafkaAvroSerializer();
keyDeserializer.configure(serdeConfig, true);
keySerializer.configure(serdeConfig, true);
Serde<Integer> keySerde = (Serde<Integer>)(Serde)Serdes.serdeFrom(keySerializer, keyDeserializer);
Esto hizo que el compilador producir una advertencia (no lo hace como el (Serde<Integer>)(Serde)
casting) pero que me permite el uso
Consumed.with(keySerde, userSerde);
y obtener un número entero como la clave. Esto funciona muy bien y mi aplicación se comporta como se esperaba (genial !!!). Pero ahora quiero definir serde predeterminado de la clave / valor y no puedo conseguir que funcione.
Ajuste de la serde valor predeterminado es simple:
streamsConfiguration.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, SpecificAvroSerde.class);
Sin embargo no puedo encontrar la manera de definir el serde clave predeterminada.
Lo intenté
streamsConfiguration.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, keySerde.getClass().getName());
Produce error de ejecución: No se pudo encontrar un constructor público sin argumentos para org.apache.kafka.common.serialization.Serdes $ WrapperSerdestreamsConfiguration.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, SpecificAvroSerde.class);
Produce error de ejecución: java.lang.Integer no se puede convertir a org.apache.avro.specific.SpecificRecord
¿Qué me estoy perdiendo? Gracias.
Actualización (versión 5.5 y posteriores)
Confluentes versión 5.5
añade soporte nativo para los tipos primitivos a través de Avro PrimitiveAvroSerde
(cf. https://github.com/confluentinc/schema-registry/blob/5.5.x/avro-serde/src/main/java/io/confluent/kafka/streams /serdes/avro/PrimitiveAvroSerde.java )
Respuesta original (versión 5.4 o más) :
Es un problema conocido. Avro tipos primitivos no funcionan bien con AvroSerdes de confluentes, debido a que el SerDes trabaja con GenericAvroRecord
y SpecificAvroRecord
solamente.
Por lo tanto, la construcción es el propietario Serde basado en KafkaAvroSerializer
y KafkaAvroDeserializer
es el enfoque correcto. Para ser capaz de pasar esto en la configuración por defecto Serde, no se puede utilizar Serdes.serdeFrom
debido a que la información de tipo se pierde debido al tipo genrics borrado.
Sin embargo, se puede implementar propia clase que se extiende Serde
interfaz en lugar y pasar la clase personalizada en la configuración:
public class MySerde extends Serde<Integer> {
// use KafkaAvroSerializer and KafkaAvroDeserializer and cast `Object` to `Integer`
}
config.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, MySerde.class);