我有一个包含隐式转换的实用程序对象:
object Util {
implicit class SparkView(sc: SparkContext) {
def do(): Unit = sc.parallelize(1 to 10).foreach {
doSomething()
}
}
def doSomething(): Unit
}
开箱即用:
val sc = new SparkContext()
sc.do()
但是,当我将上面的Util实现更改为略有不同的内容时:
class Util {
implicit class SparkView(sc: SparkContext) {
def do(): Unit = sc.parallelize(1 to 10).foreach {
doSomething()
}
}
def doSomething(): Unit
}
case object Util extends Util
它的相同用法会产生以下错误:
> Task not serializable org.apache.spark.SparkException: Task not
> serializable at
> org.apache.spark.util.ClosureCleaner$.ensureSerializable(ClosureCleaner.scala:340)
> at
> org.apache.spark.util.ClosureCleaner$.org$apache$spark$util$ClosureCleaner$$clean(ClosureCleaner.scala:330)
> at
> org.apache.spark.util.ClosureCleaner$.clean(ClosureCleaner.scala:156)
> ...
> Caused by: java.io.NotSerializableException:
> my.package.Util$SparkView
> Serialization stack:
> - object not serializable (class: my.package.Util$SparkView, value:
> my.package.Util$SparkView@4f03729f)
事实证明,在第二种情况下,函数doSomething()被序列化并附带无用的东西(实际的函数签名变为this.$outer.doSomething()
)。立即修复将声明SparkView的所有实例都是瞬态的,这样它就不会被序列化和发送,并且可以从头开始从单例Util读取函数doSomething。我该如何轻松实现?
答案 0 :(得分:1)
Spark ClosureCleaner不是序列化闭包的可靠工具。
Closure将this
与所有字段一起捕获,然后ClosureCleaner尝试使用一些没有保证的启发式方法来取消未使用的字段。
因此,只需保持范围较小且代码简单,并希望它能够正常工作。
此外,您无法使SparkView
成为瞬态,因为瞬态是字段的属性,而不是对象。