我一直在尝试序列化 avro 通用记录并生成 avro 序列化数据以发送到 kafka。主要目标是不使用融合模式注册表来存储模式,而是将模式与序列化数据一起发送,以便可以从 kafka 主题中提取并反序列化。
以下是 AvroSerializer 用于生成 Avro 数据的部分。
@Override
public byte[] serialize(String topic, T data) {
try {
byte[] result = null;
if (data != null) {
LOGGER.debug("data='{}'", data);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
BinaryEncoder binaryEncoder =
EncoderFactory.get().binaryEncoder(byteArrayOutputStream, null);
DatumWriter<GenericRecord> datumWriter = new GenericDatumWriter<>(data.getSchema());
datumWriter.setSchema(data.getSchema());
datumWriter.write(data, binaryEncoder);
binaryEncoder.flush();
byteArrayOutputStream.close();
result = byteArrayOutputStream.toByteArray();
}
return result;
} catch (IOException ex) {
throw new SerializationException(
"Can't serialize data='" + data + "' for topic='" + topic + "'", ex);
}
}
kafka 中的序列化数据如下所示。
AvroDeserializer 部分如下所示。
@Override
public T deserialize(String topic, byte[] data) {
GenericRecord person = null;
try {
T result = null;
if (data != null) {
LOGGER.debug("data='{}'", DatatypeConverter.printHexBinary(data));
Schema schema = Schema.parse(schemaString);
DatumReader<GenericRecord> datumReader = new GenericDatumReader<GenericRecord>(schema);
Decoder decoder = DecoderFactory.get().binaryDecoder(data, null);
result = (T) datumReader.read(null, decoder);
LOGGER.debug(result.getSchema().toString());
LOGGER.debug("deserialized data='{}'", result);
}
return result;
} catch (Exception ex) {
throw new SerializationException(
"Can't deserialize data '" + Arrays.toString(data) + "' from topic '" + topic + "'", ex);
}
}
制作人如下图
public class KafkaAvroProducerUtil {
public Future<RecordMetadata> produceTokafka(GenericRecord object) throws IOException {
Properties properties = new Properties();
// normal producer
properties.setProperty("bootstrap.servers", "127.0.0.1:9092");
properties.setProperty("acks", "all");
properties.setProperty("retries", "10");
// avro part
properties.setProperty("key.serializer", StringSerializer.class.getName());
properties.setProperty("value.serializer", AvroSerializer.class.getName());
String topic = "avro";
Producer<String, GenericRecord> producer = new KafkaProducer<String, GenericRecord>(properties);
ProducerRecord<String, GenericRecord> producerRecord = new ProducerRecord<String, GenericRecord>(
topic, object
);
Future<RecordMetadata> data = producer.send(producerRecord, new Callback() {
@Override
public void onCompletion(RecordMetadata metadata, Exception exception) {
if (exception == null) {
System.out.println(metadata);
} else {
exception.printStackTrace();
}
}
});
producer.flush();
producer.close();
return data;
}
当我尝试反序列化它时,它说需要模式。我所理解的问题是,当您看到上图中的数据(在 cmd 上运行的消费者的快照)时,架构不会随之发送。如何将架构与数据一起发送,以便我可以对与数据一起发送的架构进行反序列化。
答案 0 :(得分:1)
编辑:根据@OneCricketeer 和@ChinHuang 的建议,我以两种方式接近答案。
下面解释了这两种方法。但是标题方法的答案如下所示。
方法 1:随数据一起发送模式
在这种方法中,我将 Avro 模式序列化为字符串和分隔符,然后将它们发送到 kafka 主题,同时添加数据。
在从 kafka 主题读取数据后进行反序列化时,使用分隔符将字节数组拆分为架构和数据。然后我会将架构字节转换回架构,然后使用该架构反序列化数据。
该方法的缺点:正如@OneCricketeer 所说
方法 2:在标头中发送架构
这里不是随数据一起发送模式,而是在标题中发送模式。
Serializer 类中的方法如下所示。
@Override
public byte[] serialize(String topic, T data) {
return null;
}
public byte[] serialize(String topic, Headers headers, T data) {
try {
byte[] result = null;
byte[] payload = null;
if (data != null) {
LOGGER.debug("data='{}'", data);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
BinaryEncoder binaryEncoder =
EncoderFactory.get().binaryEncoder(byteArrayOutputStream, null);
byte[] schemaBytes = data.getSchema().toString().getBytes();
DatumWriter<GenericRecord> datumWriter = new GenericDatumWriter<>(data.getSchema());
datumWriter.setSchema(data.getSchema());
datumWriter.write(data, binaryEncoder);
binaryEncoder.flush();
byteArrayOutputStream.close();
result = byteArrayOutputStream.toByteArray();
ByteArrayOutputStream outputStream2 = new ByteArrayOutputStream( );
outputStream2.write( result );
payload = outputStream2.toByteArray( );
headers.add("schema",schemaBytes);
}
LOGGER.info("headers added");
return payload;
} catch (IOException ex) {
throw new SerializationException(
"Can't serialize data='" + data + "' for topic='" + topic + "'", ex);
}
}
Deserializer 方法如下所示。
@Override
public T deserialize(String topic, byte[] data) {
return null
}
public T deserialize(String topic, Headers headers, byte[] data) {
try {
T result = null;
if (data != null) {
LOGGER.debug("data='{}'", DatatypeConverter.printHexBinary(data));
Header header = headers.lastHeader("schema");
String schemaString2 = new String(header.value());
Schema schema = Schema.parse(schemaString2);
DatumReader<GenericRecord> datumReader = new GenericDatumReader<GenericRecord>(schema);
DataFileReader<GenericRecord> dataFileReader = null;
Decoder decoder = DecoderFactory.get().binaryDecoder(data, null);
result = (T) datumReader.read(null, decoder);
LOGGER.debug(result.getSchema().toString());
LOGGER.debug("deserialized data='{}'", result);
}
return (T) result;
} catch (Exception ex) {
throw new SerializationException(
"Can't deserialize data '" + Arrays.toString(data) + "' from topic '" + topic + "'", ex);
}
}