Spark Streaming中的序列化问题

时间:2016-09-26 18:12:37

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

我很担心Spark如何处理引擎盖下的数据。例如,当我运行流作业并应用foreachRDD时,行为取决于是从外部作用域捕获变量还是在内部初始化变量。

val sparkConf = new SparkConf()
dStream.foreachRDD(rdd => {
    val spark = SparkSession.builder.config(sparkConf).getOrCreate()
    ...
})

在这种情况下,我得到一个例外:

  

java.io.NotSerializableException:org.apache.spark.streaming.kafka.DirectKafkaInputDStream的对象$ DirectKafkaInputDStreamCheckpointData可能作为RDD操作关闭的一部分进行序列化。这是因为正在从闭包内引用DStream对象。请在此DStream中重写RDD操作以避免这种情况。这已被强制执行,以避免使用不必要的对象使Spark任务膨胀。

但如果我将sparkConf移到里面,一切似乎都很好:

dStream.foreachRDD(rdd => {
    val sparkConf = rdd.sparkContext.getConf
    val spark = SparkSession.builder.config(sparkConf).getOrCreate()
    ...
})

这对我来说很奇怪,因为我认为foreachRDD在驱动程序节点上运行,所以我没想到会有什么不同。

现在,如果我将SQL会话和配置同时移到foreachRDD之外,它再次正常工作:

val sparkConf = new SparkConf()
val spark = SparkSession.builder.config(sparkConf).getOrCreate()
dStream.foreachRDD(rdd => {
    val df = spark.read.json(rdd)
    ...
})

Spark文档中的snippet建议使用以前的版本(其中配置和SQL上下文都是在foreachRDD中创建的),这对我来说似乎效率低下:如果可能的话,为什么要为每个批次创建它们?创造了一次?

有人可以解释为什么抛出异常以及创建SQL上下文的正确方法是什么?

3 个答案:

答案 0 :(得分:0)

ForeachRDD运行,顾名思义,foreach rdd在流媒体中为什么你应该在每个rdd重新创建火花上下文? 正确的方法是最后一个:

val sparkConf = new SparkConf()
val spark = SparkSession.builder.config(sparkConf).getOrCreate()
dStream.foreachRDD(rdd => {
    val df = spark.read.json(rdd)
    ...
})

答案 1 :(得分:0)

val spark = SparkSession.builder.config(sparkConf).getOrCreate() 不会创建另一个SparkSession。仅存在一个。 在worker上,只需从job中获取它即可。

答案 2 :(得分:0)

在第一种方法中,您尝试为每个不正确的分区实例化spark会话对象。

其他人回答,请使用第三种方法。但是,如果您需要使用第一种方法,则可以使用以下方法-

val sparkConf = new SparkConf()
dStream.foreachRDD(rdd => {
    lazy val spark = SparkSession.builder.config(sparkConf).getOrCreate()
    ...
})

此处懒惰评估将有助于避免多次实例化spark会话,从而避免序列化问题。

我希望这会有所帮助。