我试图了解Spark中的任务序列化是如何工作的,并且我对我已经编写的测试中的一些混合结果感到有些困惑。
我有一些测试代码(为了帖子而简化),它在多个节点上执行以下操作:
object TestJob {
def run(): Unit = {
val rdd = ...
val helperObject = new Helper() // Helper does NOT impl Serializable and is a vanilla class
rdd.map(element => {
helperObject.transform(element)
}).collect()
}
}
当我执行run()
时,该作业会以"任务不可序列化的方式进行轰炸。预期的异常,因为helperObject
不可序列化。但是,当我稍微修改一下时,就像这样:
trait HelperComponent {
val helperObject = new Helper()
}
object TestJob extends HelperComponent {
def run(): Unit = {
val rdd = ...
rdd.map(element => {
helperObject.transform(element)
}).collect()
}
}
由于某种原因,作业成功执行。有人可以帮助我理解为什么会这样吗?在上面的每种情况下,什么由Spark精确序列化并发送给工人?
我正在使用Spark版本2.1.1。
谢谢!
答案 0 :(得分:2)
有人可以帮我理解为什么会这样吗?
在您的第一个代码段中,helperObject
是在run
内声明的局部变量。因此,它将由函数关闭(提升),以便在执行此代码时,所有信息都可用,并且因为Sparks ClosureCleaner
因为尝试序列化而对你大喊大叫。
在你的第二个片段中,该值不再是方法范围中的局部变量,它是类实例的一部分(从技术上讲,这是一个对象声明,但它将转换为毕竟是JVM类)。
这在Spark中很有意义,因为集群中的所有工作节点都包含执行代码所需的JAR 。因此,不是为TestObject
完整地序列化rdd.map
,当Spark在您的某个工作程序中启动Executor进程时,它将通过TestObject
在本地加载ClassLoader
,并创建它的实例,就像非分布式应用程序中的每个其他JVM类一样。
总而言之,您没有看到这种情况爆发的原因是因为您声明类型实例的方式发生了变化,因此不再对该类进行序列化。