匿名内部类导致的Spark序列化异常

浅浅的花香味﹌ 2023-06-22 13:53 116阅读 0赞

场景

spark程序中报没有序列化的错:

  1. org.apache.spark.SparkException: Task not serializable

建一段测试代码:

  1. public class SparkTest {
  2. public static void main(String[] args) throws AnalysisException {
  3. SparkTest sparkTest = new SparkTest();
  4. sparkTest.test();
  5. }
  6. private void test() throws AnalysisException {
  7. String logFile = "D:/test/hadoop_test.txt";
  8. SparkSession spark = SparkSession.builder()
  9. .config("spark.master", "local")
  10. .appName("Simple Application")
  11. .getOrCreate();
  12. spark.sparkContext().setLogLevel("warn");
  13. Dataset<String> logData = spark.read().textFile(logFile);
  14. logData.show();
  15. Dataset<String> words = logData.flatMap(new FlatMapFunction<String, String>() {
  16. @Override
  17. public Iterator<String> call(String s) throws Exception {
  18. return Arrays.asList(s.split(" ")).iterator();
  19. }
  20. }, Encoders.STRING());
  21. words.createOrReplaceTempView("test");
  22. spark.sql("select value,count(0) sum from test group by value order by sum desc ").show();
  23. spark.stop();
  24. }
  25. }

2019121615331446.png

原因

使用了匿名内部类FlatMapFunction,导致无法序列化。

解决

2个解决方法:

1.实现Serializable接口

  1. public class SparkTest implements Serializable

2.使用λ表达式而不使用内部类

  1. Dataset<String> words = logData.flatMap((FlatMapFunction<String, String>) s -> Arrays.asList(s.split(" ")).iterator(), Encoders.STRING());

20191216153736996.png

扩展

1.匿名内部类和λ表达式的区别(引用自百度):

1.匿名内部类可以为任意接口创建实例——不管有多少个抽象方法,只要匿名内部类实现了所有方法即可。
但是Lambda表达式只能为函数式接口创建实例。

2.匿名内部类可以为抽象类甚至普通类创创建实例,
但lambda表达式只能为函数式接口创建实例。

3.匿名内部类实现的抽象方法体允许调用接口中的默认方法,
但Lambda表达式的代码块不允许调用接口中的默认方法。

2.匿名内部类和λ表达式在序列化时的区别:

使用匿名内部类编译出的class文件一般都会自动生成一个类且以$+数字结尾,其本质是一个实现了接口的类,本次使用FlatMapFunction这个接口作为匿名内部类时也生成了这样的一个类:

20191216155311584.png

编译一下:

  1. class SparkTest$1 implements FlatMapFunction {
  2. // $FF: synthetic field
  3. final SparkTest this$0;
  4. SparkTest$1(SparkTest this$0) {
  5. this.this$0 = this$0;
  6. }
  7. public Iterator call(String s) throws Exception {
  8. return Arrays.asList(s.split(" ")).iterator();
  9. }
  10. }

它实现了FlatMapFunction这个接口,并且持有一个sparkTest的对象,而调用时总是和这个对象相关的,但是呢,SparkTest却没有实现序列化的接口,所以this$0不是可序列化的,所以会出现一开始序列化失败的问题。

使用λ表达式时,其本质也是生成了一个类,但是呢并没有生成class文件,所以是看不到的,但是呢可以通过开启-Djdk.internal.lambda.dumpProxyClasses来生成lambda的一个代理类文件就可以查看了:

  1. final class SparkTest$$Lambda$2011 implements FlatMapFunction {
  2. private SparkTest$$Lambda$2011() {
  3. }
  4. @Hidden
  5. public Iterator call(Object var1) {
  6. return SparkTest.lambda$test$1fbfce19$1((String)var1);
  7. }
  8. private final Object writeReplace() {
  9. return new SerializedLambda(SparkTest.class, "org/apache/spark/api/java/function/FlatMapFunction", "call", "(Ljava/lang/Object;)Ljava/util/Iterator;", 6, "spark/SparkTest", "lambda$test$1fbfce19$1", "(Ljava/lang/String;)Ljava/util/Iterator;", "(Ljava/lang/String;)Ljava/util/Iterator;", new Object[0]);
  10. }
  11. }

这个类是实现了FlatMapFunction的,而FlatMapFunction extends Serializable,并且它没有需要序列化的字段,所以序列化是没有问题的。

发表评论

表情:
评论列表 (有 0 条评论,116人围观)

还没有评论,来说两句吧...

相关阅读

    相关 匿名内部

    匿名内部类是内部类的简写格式。 定义匿名内部类的前提:   内部类必须是继承一个类或者实现一个接口。    匿名内部类的格式: new 父类或者接口() { /

    相关 java 内部嵌套序列异常

    问题描述 各个服务都正常启动,且可以正常调用,无任何异常打印信息。 但是当服务间调用时,含有内部类嵌套的dto无法被序列化,导致接口请求接口正常返回,但是无法被正常接