流式传输到RDD到DataFrame到CSV

时间:2017-11-12 13:10:14

标签: scala apache-spark spark-streaming

我真的在这里绝望了。

我要做的是捕获流,聚合流数据几秒钟,然后将其保存为CSV文件。

val sparkSession : SparkSession = SparkSession.builder()
  .master("local[*]")
  .appName("Streaming")
  .config(conf)
  //.enableHiveSupport()
  .getOrCreate()

所以,我正在捕捉流

val lines = streamingContext.socketTextStream(HOST, PORT)
val linesMessage = lines.map(_.split(DELIMITER)(1))

并计算事件

val counts = linesMessage.map(tag => (tag, 1))
        .reduceByKeyAndWindow({ (x, y) => x + y }, { (x, y) => x - y }, Seconds(EVENT_PERIOD_SECONDS*4), Seconds(EVENT_PERIOD_SECONDS))

到目前为止工作。

现在,我想将每个windowLength保存在CSV文件中,然后卡在那里:

  val schema = new StructType()
    .add(StructField("text", StringType, true))
    .add(StructField("count", IntegerType, true))

  counts.foreachRDD(rdd =>
  {
    //rdd.saveAsTextFile(CHECKPOINT_DIR + "/output_" + sdf.format(System.currentTimeMillis()))

    val df = sparkSession.createDataFrame(rdd.map(attributes => Row(attributes._1, attributes._2)), schema)
    df.write.format("csv").save(CHECKPOINT_DIR + "/output_" + sdf.format(System.currentTimeMillis()))
  })

有人可以帮助我吗?

抱歉,收到错误:

当我运行rdd.saveAsTextFile时,它会创建必须合并的各种纯文本文件。

通过创建createDataFrame会出现此错误

17/11/12 23:06:04 ERROR JobScheduler: Error running job streaming job 1510490490000 ms.1

java.lang.NullPointerException
    at org.apache.spark.sql.SparkSession.sessionState$lzycompute(SparkSession.scala:128)
    at org.apache.spark.sql.SparkSession.sessionState(SparkSession.scala:126)
    at org.apache.spark.sql.Dataset$.ofRows(Dataset.scala:65)
    at org.apache.spark.sql.SparkSession.createDataFrame(SparkSession.scala:578)
    at org.apache.spark.sql.SparkSession.createDataFrame(SparkSession.scala:335)
    at main.scala.Main$$anonfun$main$scala$Main$$functionToCreateContext$1$1.apply(Main.scala:152)
    at main.scala.Main$$anonfun$main$scala$Main$$functionToCreateContext$1$1.apply(Main.scala:146)

第146行是sparkSession.createDataFrame行

2 个答案:

答案 0 :(得分:0)

可能的原因和解决方法

问题可能来自sparkSession,可能会与streamingContext分开。

可能值得尝试从streamingContext获取spark会话以确保两者共享相同的配置:

counts.foreachRDD(rdd => {
    val spark = sparkSession.builder.config(streamingContext.sparkContext.getConf).getOrCreate()
    val df = spark.createDataFrame(rdd.map(attributes => Row(attributes._1, attributes._2)), schema)
    ...
})

<强> Partioning

如评论中所述,如果您的目标是减少Spark创建的文件数量,则只需在直接提供给您的函数的repartition上使用RDD

counts.foreachRDD(rdd => {
    rdd.repartition(10)
       .saveAsTextFile(CHECKPOINT_DIR + "/output_" + sdf
       .format(System.currentTimeMillis()))
})

repartition应该非常谨慎地使用,因为您需要对结果分区的大小进行很好的估计,以避免不足或过大。

答案 1 :(得分:0)

我通过更改我的代码来“解决”它:

  linesFilter.window(Seconds(EVENT_PERIOD_SECONDS*WRITE_EVERY_N_SECONDS), Seconds(EVENT_PERIOD_SECONDS*WRITE_EVERY_N_SECONDS)).foreachRDD { (rdd, time) =>
    if (rdd.count() > 0) {
      rdd
        .coalesce(1,true)
        .map(_.replace(DELIMITER_STREAM, DELIMITER_OUTPUT))
        //.map{_.mkString(DELIMITER_OUTPUT) }
        .saveAsTextFile(CHECKPOINT_DIR + "/output/o_" + sdf.format(System.currentTimeMillis()))
    }
  }