免责声明:刚开始玩Spark。
我很难理解着名的“任务不可序列化”的例外情况,但我的问题与我在SO上看到的情况略有不同(或者我认为)。
我有一个很小的自定义RDD(TestRDD
)。它有一个字段,用于存储其类未实现Serializable(NonSerializable
)的对象。我已经设置了“spark.serializer”配置选项来使用Kryo。但是,当我在RDD上尝试count()
时,我会得到以下结果:
Caused by: java.io.NotSerializableException: com.complexible.spark.NonSerializable
Serialization stack:
- object not serializable (class: com.test.spark.NonSerializable, value: com.test.spark.NonSerializable@2901e052)
- field (class: com.test.spark.TestRDD, name: mNS, type: class com.test.spark.NonSerializable)
- object (class com.test.spark.TestRDD, TestRDD[1] at RDD at TestRDD.java:28)
- field (class: scala.Tuple2, name: _1, type: class java.lang.Object)
- object (class scala.Tuple2, (TestRDD[1] at RDD at TestRDD.java:28,<function2>))
at org.apache.spark.serializer.SerializationDebugger$.improveException(SerializationDebugger.scala:40)
at org.apache.spark.serializer.JavaSerializationStream.writeObject(JavaSerializer.scala:46)
at org.apache.spark.serializer.JavaSerializerInstance.serialize(JavaSerializer.scala:100)
at org.apache.spark.scheduler.DAGScheduler.submitMissingTasks(DAGScheduler.scala:1009)
at org.apache.spark.scheduler.DAGScheduler.org$apache$spark$scheduler$DAGScheduler$$submitStage(DAGScheduler.scala:933)
当我查看DAGScheduler.submitMissingTasks
时,我发现它在我的RDD上使用了它的闭包序列化器,这是Java序列化器,而不是我期望的Kryo序列化器。我已经读过Kryo有序列化闭包的问题,而Spark总是使用Java序列化程序进行闭包,但我不太明白闭包是如何发挥作用的。我在这里所做的就是:
SparkConf conf = new SparkConf()
.setAppName("ScanTest")
.setMaster("local")
.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer");
JavaSparkContext sc = new JavaSparkContext(conf);
TestRDD rdd = new TestRDD(sc.sc());
System.err.println(rdd.count());
也就是说,没有映射器或任何需要序列化闭包的东西。 OTOH这是有效的:
sc.parallelize(Arrays.asList(new NonSerializable(), new NonSerializable())).count()
按预期使用Kryo序列化程序,不涉及闭包序列化程序。如果我没有将序列化程序属性设置为Kryo,我也会在这里得到一个例外。
我感谢任何解释闭包来源的指针以及如何确保我可以使用Kryo来序列化自定义RDD。
更新:此处为TestRDD
,其中包含非序列化字段mNS
:
class TestRDD extends RDD<String> {
private static final ClassTag<String> STRING_TAG = ClassManifestFactory$.MODULE$.fromClass(String.class);
NonSerializable mNS = new NonSerializable();
public TestRDD(final SparkContext _sc) {
super(_sc,
JavaConversions.asScalaBuffer(Collections.<Dependency<?>>emptyList()),
STRING_TAG);
}
@Override
public Iterator<String> compute(final Partition thePartition, final TaskContext theTaskContext) {
return JavaConverters.asScalaIteratorConverter(Arrays.asList("test_" + thePartition.index(),
"test_" + thePartition.index(),
"test_" + thePartition.index()).iterator()).asScala();
}
@Override
public Partition[] getPartitions() {
return new Partition[] {new TestPartition(0), new TestPartition(1), new TestPartition(2)};
}
static class TestPartition implements Partition {
final int mIndex;
public TestPartition(final int theIndex) {
mIndex = theIndex;
}
public int index() {
return mIndex;
}
}
}
答案 0 :(得分:7)
当我查看
DAGScheduler.submitMissingTasks
时,我看到它使用了 我的RDD上的闭包序列化程序,它是Java序列化程序,而不是 我期待的Kryo序列化器。
SparkEnv
支持两个序列化程序,一个名为serializer
,用于序列化数据,检查点,工作人员之间的消息传递等,并在spark.serializer
配置标志下可用。另一个在closureSerializer
下被称为spark.closure.serializer
,它用于检查您的对象实际上是否可序列化,并且可以配置为Spark&lt; = 1.6.2(但JavaSerializer
除了JavaSerializer
之外没有其他任何工作并且从2.0.0及以上硬编码到JavaSerializer
。
Kryo闭包序列化程序有一个使其无法使用的错误,您可以在SPARK-7708下看到该错误(这可能会在Kryo 3.0.0中修复,但Spark目前已使用特定版本的Chill修复,这是固定在Kryo 2.2.1)。此外,对于Spark 2.0.x,JavaSerializer现在已修复而不是可配置(您可以看到它in this pull request)。这意味着我们有效地使用spark.serializer
进行闭包序列化。
我们使用一个序列化程序来提交任务和其他工序之间的序列化数据是不是很奇怪?当然,但这就是我们所拥有的。
总而言之,如果您正在设置SparkContext.registerKryoClasses
配置,或使用JavaSerializer
,那么您将在Spark中使用Kryo进行大部分序列化。话虽如此,为了检查给定的类是否可序列化并将任务序列化到工作者,Spark将使用"Uncaught SoapFault exception: [4] Resource path is not callable"
。