所以,我有这样的事情。请注意,此处的baseTrait
(一个特征)是可序列化的,因此thisClass
(一个Object类)也应该是可序列化的。
object thisClass extends baseTrait {
private var someVar = null
def someFunc: RDD[...] {
...
// assigned some string value or an empty string value (not null anymore)
someVar = ...
...
if (someVar != "")
someRDD.filter(x => aFunc(x, someVar))
else
...
}
在集群模式下,当我调用someFunc
函数(由于thisClass
是一个Object类,这是一个静态方法)时,我得到一个空指针异常,我认为这与{{ 1}}未正确序列化。因为当我这样做时,它可以在群集模式下完美运行。
someVar
知道if (someVar != "") {
val someVar_ = someVar
someRDD.filter(x => aFunc(x, someVar_))
}
首先可序列化时原始代码出了什么问题吗?
我的猜测是,使用另一个类中的可序列化类的变量很好,但是如果您尝试在该类中使用它,则可能会遇到问题,因为在这种情况下,运行时将尝试序列化从中调用闭包的同一类。你觉得呢?
答案 0 :(得分:3)
在这种情况下,您不会遇到序列化问题。
基本上,在集群模式下发生的事情是thisClass.someFunc
从未真正在远程执行者的JVM中执行。在执行程序上,实例化thisClass
,并为someVar
分配了null
。然后,在thisClass
对象处于该状态时,spark框架直接在该执行程序的数据分区中可用的记录上执行lambda函数。
避免这种情况的一种方法是将对someVar
的赋值移动到thisClass
对象的主体中。这样做将在实例化对象时立即将值分配给someVar
。请记住,该代码将在集群中的每个 执行程序上执行。
如果这不可能,则另一种选择是将RDD[T]
映射到RDD[(T, String)]
,其中每条记录的字符串为someVar
,然后您的过滤器可能类似于{ {1}}。此方法将使用更多的内存,因为您将拥有.filter(x => aFunc(x._1, x._2))
值的许多副本。