【问题标题】:Trouble with deserializing Avro data in Scala在 Scala 中反序列化 Avro 数据的问题
【发布时间】:2018-12-10 01:19:14
【问题描述】:

我正在 Scala 中构建一个 Apache Flink 应用程序,它从 Kafka 总线读取流数据,然后对其执行汇总操作。来自 Kafka 的数据是 Avro 格式,需要一个特殊的反序列化类。我发现了这个 scala 类 AvroDeserializationScehema (http://codegists.com/snippet/scala/avrodeserializationschemascala_saveveltri_scala):

package org.myorg.quickstart
import org.apache.avro.io.BinaryDecoder
import org.apache.avro.io.DatumReader
import org.apache.avro.io.DecoderFactory
import org.apache.avro.reflect.ReflectDatumReader
import org.apache.avro.specific.{SpecificDatumReader, SpecificRecordBase}
import org.apache.flink.api.common.typeinfo.TypeInformation
import org.apache.flink.api.java.typeutils.TypeExtractor
import org.apache.flink.api.common.serialization._
import java.io.IOException

class AvroDeserializationSchema[T](val avroType: Class[T]) extends DeserializationSchema[T] {
  private var reader: DatumReader[T] = null
  private var decoder : BinaryDecoder = null

  def deserialize(message: Array[Byte]): T = {
    ensureInitialized()
    try {
      decoder = DecoderFactory.get.binaryDecoder(message, decoder)
      reader.read(null.asInstanceOf[T], decoder)
    }
    catch {
      case e: IOException => {
        throw new RuntimeException(e)
      }
    }
  }

  def isEndOfStream(nextElement: T): Boolean = false


  def getProducedType: TypeInformation[T] = TypeExtractor.getForClass(avroType)

  private def ensureInitialized() {
    if (reader == null) {
      if (classOf[SpecificRecordBase].isAssignableFrom(avroType)) {
        reader = new SpecificDatumReader[T](avroType)
      }
      else {
        reader = new ReflectDatumReader[T](avroType)
      }
    }
  }
}

在我的流媒体类中,我使用如下:

val stream = env
        .addSource(new FlinkKafkaConsumer010[String]("test", new 
AvroDeserializationSchema[DeviceData](Class[DeviceData]), properties))

其中 DeviceData 是在同一个项目中定义的 Scala 案例类

/** Case class to hold the Device data. */
case class DeviceData(deviceId: String,
                    sw_version: String,
                    timestamp: String,
                    reading: Double
                   )

我在编译 StreamingKafkaClient.scala 类时收到以下错误

Error:(24, 102) object java.lang.Class is not a value
        .addSource(new FlinkKafkaConsumer010[String]("test", new 
AvroDeserializationSchema[DeviceData](Class[DeviceData]), properties))

也试过了

val stream = env
        .addSource(new FlinkKafkaConsumer010[String]("test", new 
AvroDeserializationSchema[DeviceData](classOf[DeviceData]), properties))

这样我得到一个不同的错误:

Error:(21, 20) overloaded method constructor FlinkKafkaConsumer010 with alternatives:
  (x$1: java.util.regex.Pattern,x$2: org.apache.flink.streaming.util.serialization.KeyedDeserializationSchema[String],x$3: java.util.Properties)org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer010[String] <and>
  (x$1: java.util.regex.Pattern,x$2: org.apache.flink.api.common.serialization.DeserializationSchema[String],x$3: java.util.Properties)org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer010[String] <and>
  (x$1: java.util.List[String],x$2: org.apache.flink.streaming.util.serialization.KeyedDeserializationSchema[String],x$3: java.util.Properties)org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer010[String] <and>
  (x$1: java.util.List[String],x$2: org.apache.flink.api.common.serialization.DeserializationSchema[String],x$3: java.util.Properties)org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer010[String] <and>
  (x$1: String,x$2: org.apache.flink.streaming.util.serialization.KeyedDeserializationSchema[String],x$3: java.util.Properties)org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer010[String] <and>
  (x$1: String,x$2: org.apache.flink.api.common.serialization.DeserializationSchema[String],x$3: java.util.Properties)org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer010[String]
 cannot be applied to (String, org.myorg.quickstart.AvroDeserializationSchema[org.myorg.quickstart.DeviceData], java.util.Properties)
        .addSource(new FlinkKafkaConsumer010[String]("test", new AvroDeserializationSchema[DeviceData](classOf[DeviceData]), properties))

我是 Scala 的新手(这是我的第一个 Scala 程序),所以我知道我在这里缺少一些基本的东西。当我尝试学习 Scala 时,有人可以指出我做错了什么。我的意图是基本上将 avro 编码数据从 Kafka 读入 Flink,并对流数据进行一些操作。我找不到任何使用 AvroDeserializationSchema 类的例子,在我看来这是应该内置在 Flink 包中的东西。

【问题讨论】:

  • 碰巧,您的 Avro 数据是否来自 Confluent Schema Registry,因为无论如何这不会完全按照所写的方式工作。见stackoverflow.com/a/45710854/2308683另外,如果你知道Java而不是Scala,最好使用Java 8
  • 谢谢,但我只想使用 Scala。
  • 你看到那个链接了吗?它使用斯卡拉。而你没有回答我的第一个问题
  • 我会再次查看链接。不,我没有使用 Confluent Registry Schema。

标签: scala apache-kafka apache-flink avro


【解决方案1】:

为了在 Scala 中获取类对象,您需要classOf[DeviceData],而不是Class[DeviceData]

new AvroDeserializationSchema[DeviceData](classOf[DeviceData])

我找不到任何使用 AvroDeserializationSchema 类的示例

I found one (in Java)

另外,看起来在 Flink 1.6 版本中,他们会添加这个类,而不是你从其他地方复制。 FLINK-9337 & FLINK-9338

如 cmets 中所述,如果您想使用 Confluent Avro Schema Registry 而不是给出类类型,see this answer,或参考上述 Github 链接中的代码

此外,如果您正在运行 Kafka 0.11+(或 Confluent 3.3+),那么理想情况下,您应该使用 FlinkKafkaConsumer011 以及您要反序列化到的类

new FlinkKafkaConsumer011[DeviceData]

【讨论】:

  • 编辑了我原来的问题,包括我用“classOf[DeviceData]”得到的编译器错误。刚刚看到 Java 示例 (github.com/okkam-it/flink-examples/blob/master/src/main/java/…) 正是我在 Scala 中尝试做的。
  • 您仍在使用 [String] 在您的消费者类型中,而不是您要反序列化的类型
  • 就是这样,我怎么错过了。解决了这个问题,它工作得很好,谢谢!不过,我确实有下一个问题,当接收到流数据时,程序立即崩溃,因为反序列化例程似乎需要“init”方法:线程“main”org.apache.flink.runtime.client.JobExecutionException 中的异常: java.lang.RuntimeException: java.lang.NoSuchMethodException: org.myorg.quickstart.DeviceData.()
  • 不确定那个。您有一个案例类,它应该构建一个类初始化方法。随意使用复选标记接受此答案并发布新问题
  • 接受这个答案!总而言之,我使用了 FlinkKafkaConsumer011(尽管我认为这个问题不需要它)并且有效的代码是: val stream = env .addSource(new FlinkKafkaConsumer011[DeviceData]("test", new AvroDeserializationSchema[DeviceData](classOf[DeviceData] ]), properties)) 我将为与 init 方法相关的错误创建一个新问题。
猜你喜欢
  • 2017-11-20
  • 2018-08-11
  • 1970-01-01
  • 1970-01-01
  • 2019-03-03
  • 1970-01-01
  • 2017-01-22
  • 2019-02-12
  • 2019-09-21
相关资源
最近更新 更多