Avro with Kafka - 通过更改架构进行反序列化

时间:2016-04-18 14:48:41

标签: apache-kafka avro

基于Avro架构,我生成了一个类(Data)来处理适合于架构的类 之后我编码数据并使用kafka

发送到其他应用程序“A”
Data data; // <- The object was initialized before . Here it is only the declaration "for example"
EncoderFactory encoderFactory = EncoderFactory.get();
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        BinaryEncoder encoder = encoderFactory. directBinaryEncoder(out, null);                    
        DatumWriter<Tloog> writer;                  
        writer = new SpecificDatumWriter<Data>( Data.class);
        writer.write(data, encoder);
        byte[] avroByteMessage = out.toByteArray();

另一方面(在应用程序“A”中)我通过实现Deserializer

来对数据进行deserilize
class DataDeserializer implements Deserializer<Data> {
    private String encoding = "UTF8";

    @Override
    public void configure(Map<String, ?> configs, boolean isKey) {
        // nothing to do
    }

    @Override
    public Tloog deserialize(String topic, byte[] data) {
        try {
            if (data == null)
            {
                return null;
            }
            else
            {
                        DatumReader<Tloog> reader = new SpecificDatumReader<Data>( Data.class);
                        DecoderFactory decoderFactory = DecoderFactory.get();
                        BinaryDecoder decoder = decoderFactory.binaryDecoder( data, null);
                        Data decoded = reader.read(null, decoder);
                        return decoded;
            }
        } catch (Exception e) {
            throw new SerializationException("Error when deserializing byte[] to string due to unsupported encoding " + encoding);
        }
    }

问题是这种方法需要使用SpecificDatumReader,即Data类应该与应用程序代码集成...这可能会有问题 - 架构可能会发生变化,因此Data类应该重新生成并再次集成 2个问题:

  1. 我应该在应用程序中使用GenericDatumReader吗?怎么做 正确。 (我可以简单地在应用程序中保存模式)
  2. 如果数据发生变化,我还有一种简单的方法可以使用SpecificDatumReader吗?怎么可以整合出来呢?
  3. 由于

1 个答案:

答案 0 :(得分:1)

我使用GenericDatumReader - 实际上,我从中获取了我的读者类,但是你明白了。为了使用它,我将我的模式保存在一个特殊的Kafka主题中 - Schema令人惊讶。消费者和生产者在启动时都会从这个主题中读取并配置他们各自的解析器。

如果您这样做,您甚至可以让您的消费者和生产者动态更新他们的模式,而无需重新启动它们。这对我来说是一个设计目标 - 我不想重新启动我的应用程序以添加或更改模式。这就是为什么SpecificDatumReader对我不起作用的原因,老实说为什么我首先使用Avro而不是像Thrift那样。

<强>更新

执行Avro的常规方法是将模式存储在包含记录的文件中。我不这样做,主要是因为我做不到。我使用Kafka,因此我无法直接将模式与数据一起存储 - 我必须将模式存储在单独的主题中。

我这样做,首先加载我的所有模式。你可以从文本文件中读取它们;但就像我说的那样,我是从Kafka话题中读到的。在我从Kafka读取它们之后,我有一个这样的数组:

val schemaArray: Array[String] = Array(
  """{"name":"MyObj","type":"record","fields":[...]}""",
  """{"name":"MyOtherObj","type":"record","fields":[...]}"""
)

Scala BTW道歉,但这就是我得到的。

无论如何,您需要创建一个解析器和foreach架构,解析它并创建读者和编写器,并将它们保存到Maps:

val parser = new Schema.Parser()
val schemas = Map(schemaArray.map{s => parser.parse(s)}.map(s => (s.getName, s)):_*)
val readers = schemas.map(s => (s._1, new GenericDatumReader[GenericRecord](s._2)))
val writers = schemas.map(s => (s._1, new GenericDatumWriter[GenericRecord](s._2)))
var decoder: BinaryDecoder = null

在解析实际记录之前,我会做所有这些 - 这只是配置解析器。然后,为了解码我会做的单个记录:

val byteArray: Array[Byte] = ... // <-- Avro encoded record
val schemaName: String = ... // <-- name of the Avro schema

val reader = readers.get(schemaName).get

decoder = DecoderFactory.get.binaryDecoder(byteArray, decoder)
val record = reader.read(null, decoder)