前几天在写文章相似度比较,要取每篇文章最长的若干句,然后simHash得相似度。结果问题出现了:
将文章分割成句子之后,无论用sort还是JavaPairRDD的sortByKey方法之后,只要我想拿数据,比如take(int) , top(int)等,都会有一个类似下面的序列化问题出现:
刚开始时用的Lambda表达式,报错是lambda表达式对象无法序列化:
failed in 0.032 s due to Job aborted due to stage failure: Task not serializable: java.io.NotSerializableException: com.tensor.api.org.service.spark.statistics.StatisticsServiceImpl$$Lambda$1054/1846568576
Serialization stack:
- object not serializable (class: com.tensor.api.org.service.spark.statistics.StatisticsServiceImpl$$Lambda$1054/1846568576, value: com.tensor.api.org.service.spark.statistics.StatisticsServiceImpl$$Lambda$1054/1846568576@41138857)
- element of array (index: 0)
- array (class [Ljava.lang.Object;, size 1)
- field (class: java.lang.invoke.SerializedLambda, name: capturedArgs, type: class [Ljava.lang.Object;)
- object (class java.lang.invoke.SerializedLambda, SerializedLambda[capturingClass=interface java.util.Comparator, functionalInterfaceMethod=java/util/Comparator.compare:(Ljava/lang/Object;Ljava/lang/Object;)I, implementation=invokeStatic java/util/Comparator.lambda$comparingInt$7b0bb60$1:(Ljava/util/function/ToIntFunction;Ljava/lang/Object;Ljava/lang/Object;)I, instantiatedMethodType=(Ljava/lang/Object;Ljava/lang/Object;)I, numCaptured=1])
...
...
at com.tensor.api.org.service.spark.statistics.StatisticsServiceImpl.getLongestSentences(StatisticsServiceImpl.java:71)
at SparkServiceTest.main(SparkServiceTest.java:58)
报错里面Lambda表达式后面跟了一串看不懂的数字,根本没法定位问题所在,于是将Lambda表达式全部替换成了函数对象,然后运行:
to Job aborted due to stage failure: Task not serializable: java.io.NotSerializableException: SparkServiceTest$2
Serialization stack:
- object not serializable (class: SparkServiceTest$2, value: SparkServiceTest$2@2f4daa87)
- field (class: scala.math.LowPriorityOrderingImplicits$$anon$4, name: cmp$1, type: interface java.util.Comparator)
- object (class scala.math.LowPriorityOrderingImplicits$$anon$4, scala.math.LowPriorityOrderingImplicits$$anon$4@df3d65d)
- field (class: scala.math.Ordering$$anon$1, name: $outer, type: interface scala.math.Ordering)
...
这样就清晰很多了,原来是排序时需要传入一个Comparator对象,我们找到Comparator的类定义,发现它并未extends Serializable接口。找到问题就好解决了,只需我们自己写一个SerializableComparator
/**
* 排序时需要用到{@link Comparator}接口,但它并没有实现序列化,因此总是报错。
* 在这里给它序列化一下。
*
* 没有实现任何方法,仅作为mark interface定义类型
*
* 注意,序列化并不是仅仅extends一下接口这么简单,必须十分谨慎,
* 因为要考虑很多安全、反序列化效率等问题。具体请参见《Effective Java》序列化章节
* 这里为了赶紧能用,暂时统统省略了
*
* @author lightDance
*/
public interface SerializableComparator extends Comparator, Serializable {
}
mapToPair(new PairFunction, Integer , String>(){...} , false);
这个问题坑就坑在SparkRDD的lazy机制,虽然是sort这一步出的问题,但报错却报的问题出现在take(),top()这些行动操作上。加上最开始为了简洁用lambda表达式导致无法锁定位置,被这个问题困扰好久。
总结一下解决思路:
1.查Spark序列化问题的原因:数据处理流程中使用了无法序列化的对象,特别需要注意有没有对当前类成员的引用,有的话整个类都需要序列化,有时候要配合使用@Transient注解
2.确保当前类引用没问题了,仍然有报错:将链式调用改为逐步处理,锁定报错位置
3.报错中讲Lambda表达式无法序列化,但具体哪一个Lambda表达式有问题,报错中是用一串看不懂的数字表示的:更改Lambda表达式,替换成函数对象
4.锁定是Comparator参数有问题:查看其类定义,发现它未extends Serializable接口,定义子类扩展它,然后使用时参数用自定义子类替换父类。