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


    场景

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

    org.apache.spark.SparkException: Task not serializable
    

    建一段测试代码:

    public class SparkTest {
    
        public static void main(String[] args) throws AnalysisException {
            SparkTest sparkTest = new SparkTest();
            sparkTest.test();
        }
    
        private void test() throws AnalysisException {
            String logFile = "D:/test/hadoop_test.txt";
            SparkSession spark = SparkSession.builder()
                    .config("spark.master", "local")
                    .appName("Simple Application")
                    .getOrCreate();
            spark.sparkContext().setLogLevel("warn");
            Dataset<String> logData = spark.read().textFile(logFile);
            logData.show();
            Dataset<String> words = logData.flatMap(new FlatMapFunction<String, String>() {
                @Override
                public Iterator<String> call(String s) throws Exception {
                    return Arrays.asList(s.split(" ")).iterator();
                }
            }, Encoders.STRING());
            words.createOrReplaceTempView("test");
            spark.sql("select value,count(0) sum from test group by value order by sum desc ").show();
            spark.stop();
        }
    }
    

    原因

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

    解决

    2个解决方法:

    1.实现Serializable接口

    public class SparkTest implements Serializable
    

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

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

    扩展

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

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

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

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

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

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

    编译一下:

    class SparkTest$1 implements FlatMapFunction {
    
       // $FF: synthetic field
       final SparkTest this$0;
    
    
       SparkTest$1(SparkTest this$0) {
          this.this$0 = this$0;
       }
    
       public Iterator call(String s) throws Exception {
          return Arrays.asList(s.split(" ")).iterator();
       }
    }
    

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

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

    	final class SparkTest$$Lambda$2011 implements FlatMapFunction {
        private SparkTest$$Lambda$2011() {
        }
    
        @Hidden
        public Iterator call(Object var1) {
            return SparkTest.lambda$test$1fbfce19$1((String)var1);
        }
    
        private final Object writeReplace() {
            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]);
        }
    }
    

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

  • 相关阅读:
    multiselect2side双向选择列表插件改进版
    通用权限管理平台--实现站点地图
    通用权限管理平台--系统日志
    通用权限案例平台--登录认证
    通用权限管理平台--代码结构
    通用权限管理平台--功能划分
    通用权限管理平台--架构选型
    tests
    Python 2.7_多进程获取简书专题数据(一)
    python2.7 爬取简书30日热门专题文章之简单分析_20170207
  • 原文地址:https://www.cnblogs.com/cnsec/p/13286614.html
Copyright © 2020-2023  润新知