我正在尝试创建一个Spark流,该流使用ProtoBuf编码的Kafka消息。
这是我最近几天尝试过的
import spark.implicits._
def parseLine (str: Array[Byte]): ProtoSchema = ProtoSchema.parseFrom(str)
val storageLoc: String = "/tmp/avl/output"
val checkpointLoc: String = "/tmp/avl/checkpoint"
val dfStreamReader: DataFrame = spark
.readStream
.format("kafka")
.option("kafka.bootstrap.servers", brokers)
.option("failOnDataLoss", value = false)
.option("subscribe", topics)
.load()
val dfStreamReaderValues: Dataset[Array[Byte]] = dfStreamReader.map(row => row.getAs[Array[Byte]]("value"))
val rddProtoSchema: Dataset[ProtoSchema] = dfStreamReaderValues.map(str => parseLine(str))
val dfRaw: DataFrame = spark.sqlContext.protoToDataFrame(rddProtoSchema.rdd)
val streamWriterAirline: StreamingQuery = dfRaw.writeStream
.format("parquet")
.option("path", storageLoc)
.option("checkpointLocation", checkpointLoc)
.outputMode(Append)
.trigger(ProcessingTime("2 seconds"))
.start()
spark.streams.awaitAnyTermination(20000)
使用scalapb,我设法解码二进制原始文件并将其转换为数据帧。但是使用流技术时,我在解析时在编译时遇到了这个异常:
val rddProtoSchema: Dataset[ProtoSchema] = dfStreamReaderValues.map(str => parseLine(str))
>>>>>
scala.ScalaReflectionException: <none> is not a term
有人可以给些提示吗?
答案 0 :(得分:1)
更新:sparksql-scalapb现在可以派生用于协议缓冲区的编码器,并且不再需要使用UDT生成器的先前方法。说明是available here。
旧答案(现在不相关):使用数据集时,Spark会尝试为消息中的每个字段查找SQL类型。 Spark不知道如何处理ScalaPB枚举(它们表示为密封特征,并由case对象扩展),因此会出现此错误。解决方法是注册枚举和用户定义的类型。可以按照以下步骤进行操作:
project/plugins.sbt
(而不是主要build.sbt
):libraryDependencies += "com.thesamet.scalapb" %% "sparksql-scalapb-gen" % "0.8.1"
检查以上版本与您使用的
sparksql-scalapb
的版本是否匹配。
PB.targets
中的build.sbt
:PB.targets in Compile := Seq(
scalapb.gen() -> (sourceManaged in Compile).value,
scalapb.UdtGenerator -> (sourceManaged in Compile).value
)
重新生成源(可能需要sbt clean
后跟sbt compile
)
在主函数中调用生成的注册函数。应该是mypackage.MyFileUdt.register()
请参阅:https://scalapb.github.io/sparksql.html#datasets-and-none-is-not-a-term