以下是当我尝试将作业分派给执行程序时导致java.io.NotSerializableException的代码。
JavaRDD<Row> rddToWrite = dataToWrite.toJavaRDD();
JavaRDD<String> stringRdd = rddToWrite.map(new Function<Row, String>() {
/**
* Serial Version Id
*/
private static final long serialVersionUID = 6766320395808127072L;
@Override
public String call(Row row) throws Exception {
return row.mkString(dataFormat.getDelimiter());
}
});
但是,当我执行以下操作时,任务已成功序列化:
JavaRDD<Row> rddToWrite = dataToWrite.toJavaRDD();
List<String> dataList = rddToWrite.collect().stream().parallel()
.map(row -> row.mkString(dataFormat.getDelimiter()))
.collect(Collectors.<String>toList());
JavaSparkContext javaSparkContext = new JavaSparkContext(sessionContext.getSparkContext());
JavaRDD<String> stringRDD = javaSparkContext.parallelize(dataList);
任何人都可以帮我指出我在这里做错了吗?
编辑: dataFormat是类中的私有成员字段,其中包含此代码的函数被写入。它是类DataFormat的一个对象,它定义了两个字段,即spark dataformat(例如“com.databricks.spark.csv”)和分隔符(例如“\ t”)。
答案 0 :(得分:3)
由new Function ...
创建的匿名类需要对封闭实例的引用,并且序列化该函数需要序列化封闭实例,包括dataFormat
和所有其他字段。如果该类未标记为Serializable
,或者具有任何不可序列化的非transient
字段,则无法正常工作。即使它确实如此,它也会默默地表现得比必要的更糟糕。
不幸的是,要完全解决这个问题,你需要创建一个命名的静态内部类(或者只是一个单独的类),它甚至不能是本地的(因为匿名和local classes in Java都不能是静态的):
static class MyFunction extends Function<Row, String> {
private String delimiter;
private static final long serialVersionUID = 6766320395808127072L;
MyFunction(String delimiter) {
this.delimiter = delimiter;
}
@Override
public String call(Row row) throws Exception {
return row.mkString(delimiter);
}
}
然后
JavaRDD<String> stringRdd = rddToWrite.map(new MyFunction(dataFormat.getDelimiter()));
答案 1 :(得分:3)
当您访问dataFormat
时,表示this.dataFormat
。
因此,spark会尝试序列化this
并遇到NotSerializableException
。
尝试制作本地副本,如:
DataFormat dataformat = this.dataformat;
JavaRDD<Row> rddToWrite = dataToWrite.toJavaRDD();
JavaRDD<String> stringRdd = rddToWrite.map(new Function<Row, String>() ...
有关详细信息,请参阅 http://spark.apache.org/docs/latest/programming-guide.html#passing-functions-to-spark