• Flink资料(4) -- 类型抽取和序列化


    类型抽取和序列化

    本文翻译自Type Extraction and Serialization

     

    Flink处理类型的方式比较特殊,包括它自己的类型描述,一般类型抽取和类型序列化框架。该文档描述这些概念并解释其机理。

     

    Java APIScala API处理类型信息的方式有根本性的区别,所以本文描述的问题仅与其中一种API相关

    一、Flink中对类型的处理

    一般处理类型时,我们并不干涉,而是让编程语言和序列化框架来自动处理类型。与之相反的,Flink想要尽可能掌握进出用户函数的数据类型的信息。

    1.    为了可以使用持久化类(POJO)以及通过成员名来grouping/joiningFlink需要数据类型信息来在job被执行前进行检查(拼写错误和类型兼容性)

    2.    信息知道得越多,编译器/优化器就可以开发出更好的序列化(serialization)和数据分布模式(data layout scheme)。这对Flinkmemory usage paradigm是非常重要的(它用于序列化从堆中出入的数据,并使得序列化开销变得十分低)

    3.    对于即将推出的逻辑程序(logic program,我们需要这些信息来识别函数的“模式(scheme)”

    4.    最后,这些信息使得用户免于考虑序列化框架,以及考虑将类型注册到这些框架中去

    二、FlinkTypeInformation

    TypeInformation是所有类型描述类的基类。它包括了一些类型的基本属性,并可以动过它来生成序列化器(serializer),特殊情况下可以生成类型的比较器。(NoteFlink中的比较器不仅仅是定义大小,它们是处理keys时的基本辅助工具)

     

    Flink对类型做出如下区分:

    1.    基本类型:所有Java基本数据类型和对应装箱类型,加上void, String, Date

    2.    基本数组和Object数组

    3.    复合类型:

    a.     Flink Java TupleFlink Java API的一部分)

    b.    Scala case 类(包括Scala Tuple

    c.     POJO类:遵循类bean模式的类

    4.    Scala辅助类型(OptionEitherListsMaps

    5.    泛型(Generic):这些类型将不会由Flink自己序列化,而是借助Kryo来序列化

     

    POJO类支持复杂类型的创建,并且在定义keys时可以使用成员的名字:dataSet.join(another).where("name").equalTo("personName")。同时,POJO类对于运行时是透明的,这使得Flink可以十分高效地处理它们。

     

    POJO类型的规则

    当以下条件满足时,Flink将以POJO类型识别一个数据类型,并允许以成员名引用:

    1.    该类是public并且独立的(即没有非静态的内部类)

    2.    该类拥有一个public的无参数构造函数

    3.    该类(以及该类的超类)的成员要么是public的,要么拥有public的符合Java beanGetterSetter命名规则的GetterSetter函数。

    三、Scala API中的类型信息

    四、Java API中的类型信息

    Java一般会清除泛型信息。仅对泛型类的子类,由子类存储泛型变量捆绑的类型。

     

    Flink对实现用户函数的(匿名anonymous)类使用反射(reflection来确定该函数的泛型参数的类型。该逻辑还包含简单的类型推论,用于函数返回的类型由输入类型决定的情况,例如如下的泛型函数:

    1 public class AppendOne<T> extends MapFunction<T, Tuple2<T, Long>>
    2 {
    3     public Tuple2<T, Long> map(T value)
    4     {
    5          return new Tuple<T, Long>(value, 1L);
    6     }
    7 }

     

    Flink并不可能在所有情况下都可靠地识别出函数的数据类型。在泛型Lambda表达式(我们试图在Java社区解决这个问题,见下)和泛型变量推断上仍然存在一些问题。

    4.1 Java API中的Type Hints

    为了帮助Flink无法重建被清除的泛型信息的情况,自版本0.9后提供了type hintJava APIType hint通知系统一个函数产生的数据集的类型。下面是一个Type hint的例子:

    DataSet<SomeType> result = dataSet
      .map(new MyGenericNonInferrableFunction<Long, SomeType>())
      .returns(SomeType.class);

     

    在本例中,returns语句通过一个类指定了产生的类型。Type hint支持如下几种方法进行类型定义:

    1.    类,针对无参数的类型(非泛型)

    2.    String,语法为returns("Tuple2<Integer, my.SomeType>"),该字符串将被分析并转换为TypeInformation对象

    3.    一个TypeInfomation对象

    4.2 针对Java 8 Lambda表达式的类型抽取

    由于Lambda表达式不涉及继承函数接口的实现类,Java 8 Lambda的类型抽取与非lambda表达式的机理并不相同。

     

    现在,Flink正尝试确定如何实现Lambda表达式,并使用Java的泛型签名(generic signature)来决定参数类型和返回类型。但是,并不是所有的编译器中的签名都是为Lambda表达式生成的(本文写作时该文档仅完全适用在Milestone 2Eclipse JDT编译器4.5及以前的编译器下)

    4.2.1 类型信息对Java Lambda的改善

    Flink的提交者之一(Timo Walther)活跃于Eclipse JDT 编译器社区和OpenJDK社区,并向编译器提交了改善类型信息对Java 8 Lambda表达式可用性的补丁。

     

    Eclipse JDT 编译器在版本4.5 M4添加了对该方面的支持,而有关OpenJDK编译器的有关方面正在讨论当中。

    4.3 POJO类型的序列化

    PojoTypeInfomation用于创建针对POJO中所有成员的序列化器。Flink自带了针对诸如intlongString等标准类型的序列化器,对于所有其他的类型,我们交给Kryo处理。

     

    如果Kryo不能处理某类型,则我们可以通过PojoTypeInfo来使用Avro来序列化POJO,为了达成该目的,我们需要调用如下接口:

     

    1 1 final ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
    2 2 env.getConfig().enableForceAvro();

     

    注意Flink自动序列化POJO对象,该对象由AvroAvro序列化器产生。

     

    如果我们想使得整个POJO类型都由Kryo序列化器处理,则我们如下设置:

    1 final ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
    2 env.getConfig().enableForceKryo();

     

    如果Kryo不能序列化该POJO对象,我们可以添加一个自定义序列化器到Kryo,使用代码如下:

    1 env.getConfig().addDefaultKryoSerializer(Class<?> type, Class<? extends Serializer<?>> serializerClass)

     

    这些方法还有许多不同的重载形式可用

     

  • 相关阅读:
    2021/1/10周总结一
    java基础复习五
    java基础复习四
    构建之法读书笔记三
    javaweb学生教务系统
    java基础复习三
    关于eclipse项目错误的一些小经验总结
    Java基础复习二
    JavaDoc文档
    2017-2018-2 20179216 《网络攻防与实践》 第四周总结
  • 原文地址:https://www.cnblogs.com/lanyun0520/p/5658765.html
Copyright © 2020-2023  润新知