在Spark结构化流中从kafka / json数据源写入损坏的数据

时间:2018-12-27 02:00:52

标签: apache-spark apache-spark-sql spark-structured-streaming

在spark批处理作业中,我通常将JSON数据源写入文件,并且可以使用DataFrame阅读器的损坏列功能在单独的位置写出损坏的数据,而另一个阅读器从同一位置写出有效数据工作。 (数据写为实木复合地板)

但是在Spark结构流中,我首先通过kafka以字符串形式读取流,然后使用from_json获取我的DataFrame。然后from_json使用JsonToStructs,它在解析器中使用FailFast模式,并且不将未解析的字符串返回到DataFrame中的列。 (请参阅参考资料中的“注意”)然后如何使用SSS将与我的架构和可能无效的JSON不匹配的损坏数据写入另一个位置?

最后,在批处理作业中,同一作业可以写入两个数据帧。但是Spark结构化流媒体需要对多个接收器进行特殊处理。然后在Spark 2.3.1(我的当前版本)中,应包括有关如何正确写入损坏的流和无效的流的详细信息...

参考:https://jaceklaskowski.gitbooks.io/mastering-spark-sql/spark-sql-Expression-JsonToStructs.html

val rawKafkaDataFrame=spark
  .readStream
  .format("kafka")
  .option("kafka.bootstrap.servers", config.broker)
  .option("kafka.ssl.truststore.location", path.toString)
  .option("kafka.ssl.truststore.password", config.pass)
  .option("kafka.ssl.truststore.type", "JKS")
  .option("kafka.security.protocol", "SSL")
  .option("subscribe", config.topic)
  .option("startingOffsets", "earliest")

  .load()

val jsonDataFrame = rawKafkaDataFrame.select(col("value").cast("string"))

// does not provide a corrupt column or way to work with corrupt
jsonDataFrame.select(from_json(col("value"), schema)).select("jsontostructs(value).*")

2 个答案:

答案 0 :(得分:0)

当您从字符串转换为json时,如果无法使用提供的架构进行解析,则它将返回null。您可以过滤空值并选择字符串。像这样的东西。

val jsonDF =  jsonDataFrame.withColumn("json", from_json(col("value"), schema))
val invalidJsonDF = jsonDF.filter(col("json").isNull).select("value")

答案 1 :(得分:0)

我只是想找出结构化流的_corrupt_record等效项。这是我想出的;希望它可以使您更接近所要寻找的东西:

// add a status column to partition our output by
// optional: only keep the unparsed json if it was corrupt
// writes up to 2 subdirs: 'out.par/status=OK' and 'out.par/status=CORRUPT'
// additional status codes for validation of nested fields could be added in similar fashion

df.withColumn("struct", from_json($"value", schema))
  .withColumn("status", when($"struct".isNull, lit("CORRUPT")).otherwise(lit("OK")))
  .withColumn("value", when($"status" <=> lit("CORRUPT"), $"value"))
  .write
  .partitionBy("status")
  .parquet("out.par")