Scala 带检查点的Spark会话空指针
我启用了将日志保存到S3的检查点。 如果检查点目录中没有文件,spark streaming工作正常,我可以看到日志文件出现在检查点目录中。然后我杀死spark streaming并重新启动它。这一次,我开始为spark会话获取NullPointerException。 简而言之,如果检查点目录中没有日志文件,spark streaming可以正常工作。但是,只要我使用检查点目录中的日志文件重新启动spark streaming,spark会话上就会出现空指针异常。 代码如下:Scala 带检查点的Spark会话空指针,scala,apache-spark,spark-streaming,checkpointing,Scala,Apache Spark,Spark Streaming,Checkpointing,我启用了将日志保存到S3的检查点。 如果检查点目录中没有文件,spark streaming工作正常,我可以看到日志文件出现在检查点目录中。然后我杀死spark streaming并重新启动它。这一次,我开始为spark会话获取NullPointerException。 简而言之,如果检查点目录中没有日志文件,spark streaming可以正常工作。但是,只要我使用检查点目录中的日志文件重新启动spark streaming,spark会话上就会出现空指针异常。 代码如下: object a
object asf {
val microBatchInterval = 5
val sparkSession = SparkSession
.builder()
.appName("Streaming")
.getOrCreate()
val conf = new SparkConf(true)
//conf.set("spark.streaming.receiver.writeAheadLog.enable", "true")
val sparkContext = SparkContext.getOrCreate(conf)
val checkpointDirectory = "s3a://bucketname/streaming-checkpoint"
println("Spark session: " + sparkSession)
val ssc = StreamingContext.getOrCreate(checkpointDirectory,
() => {
createStreamingContext(sparkContext, microBatchInterval, checkpointDirectory, sparkSession)
}, s3Config.getConfig())
ssc.start()
ssc.awaitTermination()
}
def createStreamingContext(sparkContext: SparkContext, microBatchInterval: Int, checkpointDirectory: String,spark:SparkSession): StreamingContext = {
println("Spark session inside: " + spark)
val ssc: org.apache.spark.streaming.StreamingContext = new StreamingContext(sparkContext, Seconds(microBatchInterval))
//TODO: StorageLevel.MEMORY_AND_DISK_SER
val lines = ssc.receiverStream(new EventHubClient(StorageLevel.MEMORY_AND_DISK_SER);
lines.foreachRDD {
rdd => {
val df = spark.read.json(rdd)
df.show()
}
}
ssc.checkpoint(checkpointDirectory)
ssc
}
}
同样,在我第一次运行这段代码时(检查点目录中没有日志文件),我可以看到数据帧被打印出来。
如果我使用检查点目录中的日志文件运行,我甚至看不到
println("Spark session inside: " + spark)
第一次打印出来。错误:
Exception in thread "main" java.lang.NullPointerException
at org.apache.spark.sql.SparkSession.sessionState$lzycompute(SparkSession.scala:111)
at org.apache.spark.sql.SparkSession.sessionState(SparkSession.scala:109)
at org.apache.spark.sql.DataFrameReader.<init>(DataFrameReader.scala:549)
at org.apache.spark.sql.SparkSession.read(SparkSession.scala:605)
编辑:我添加了这一行:
conf.set("spark.streaming.stopGracefullyOnShutdown","true")
这仍然没有什么区别,仍然得到NullPointerException。回答我自己的问题,这是有效的:
lines.foreachRDD {
rdd => {
val sqlContext:SQLContext = SparkSession.builder.config(rdd.sparkContext.getConf).getOrCreate().sqlContext
val df = sqlContext.read.json(rdd)
df.show()
}
}
通过从rdd.sparkContext构建的spark会话,这是一种反模式。为了让新手明确受益,spark会话可以工作。不允许在转换内创建数据集
正如Michel提到的,executor无法访问SparkSession您是否在运行之间更改了代码?设置检查点时不能更改代码。如果是这种情况,请参阅相关spark文档。您需要优雅地关闭并删除或更改检查点dir每次我第一次运行作业时,我都会清空s3a://bucketname/streaming checkpoint。然后我会按Ctrl+C关闭spark streaming。然后再次启动,然后我会得到空指针异常。我在RunsinDect之间使用了相同的代码,这解释了很多。。当代码在执行器上运行时,它无权访问val
spark
。我很惊讶你没有得到一个任务不可序列化的异常。请进一步挖掘,sessionState
是一个暂时的惰性val。这意味着每个执行器上都有一个用于初始化变量的插槽。它与驱动程序上的实例不共享同一个实例。因为它是惰性的,所以它在第一次使用时得到初始化,而第一次使用恰好是在您加载检查点时。此时,它尝试使用另一个valparentSessionState
,该值在执行器上为null,因为它也是暂时的。当我在sqlContext.read.json之后添加第二个类的另一行处理时,问题实际上会出现。例如,我在forEachRDD之外实例化了这个二级类,它所采用的参数之一是在实例化时使用sparkSession。在第二节课中,我使用sparkSession做了一些spark.sql之类的事情。这基本上意味着我必须在forEachRDD中每次实例化这个二级类,以从RDD获取spark会话。。。
lines.foreachRDD {
rdd => {
val sqlContext:SQLContext = SparkSession.builder.config(rdd.sparkContext.getConf).getOrCreate().sqlContext
val df = sqlContext.read.json(rdd)
df.show()
}
}