4.1.12 Flink-流处理框架-Flink流处理API之支持的数据类型总结

1.写在前面

        为什么会出现“不支持”的数据类型呢?因为 Flink 作为一个分布式处理框架,处理的是以数据对象作为元素的流。如果用水流来类比,那么我们要处理的数据元素就是随着水流漂动 的物体。在这条流动的河里,可能漂浮着小木块,也可能行驶着内部错综复杂的大船。要分布 式地处理这些数据,就不可避免地要面对数据的网络传输、状态的落盘和故障恢复等问题,这 就需要对数据进行序列化和反序列化。小木块是容易序列化的;而大船想要序列化之后传输, 就需要将它拆解、清晰地知道其中每一个零件的类型。

        为了方便地处理数据,Flink 有自己一整套类型系统。Flink 使用“类型信息” (TypeInformation)来统一表示数据类型。TypeInformation 类是 Flink 中所有类型描述符的基类。 它涵盖了类型的一些基本属性,并为每个数据类型生成特定的序列化器、反序列化器和比较器。

2.Flink支持的数据类型总结

        简单来说,对于常见的 Java 和 Scala 数据类型,Flink 都是支持的。Flink 在内部,Flink 对支持不同的类型进行了划分,这些类型可以在 Types 工具类中找到。

        Flink 的类型系统源码位于 org.apache.flink.api.common.typeinfo 包,让我们对上图深入追踪,看一下类的继承关系图。

        可以看到,图 1 和 图 2 是一一对应的,TypeInformation 类是描述一切类型的公共基类,它和它的所有子类必须可序列化(Serializable),因为类型信息将会伴随 Flink 的作业提交,被传递给每个执行节点。由于 Flink 自己管理内存,采用了一种非常紧凑的存储格式(见官方博文),因而类型信息在整个数据处理流程中属于至关重要的元数据。

2.1 基础数据类型

        Flink 支持所有的 Java 和 Scala 基础数据类型,Int, Double, Long, String, …所有 Java 基本类型及其包装类,再加上 Void、String、Date、BigDecimal 和 BigInteger。

DataStream<Integer> numberStream = env.fromElements(1, 2, 3, 4);
numberStream.map(data -> data * 2);

2.2 数组类型

        包括基本类型数组(PRIMITIVE_ARRAY)和对象数组(OBJECT_ARRAY)

2.3 复合数据类型

⚫ Java 元组类型(TUPLE):这是 Flink 内置的元组类型,是 Java API 的一部分。最多 25 个字段,也就是从 Tuple0~Tuple25,不支持空字段

⚫ Scala 样例类及 Scala 元组:不支持空字段

⚫ 行类型(ROW):可以认为是具有任意个字段的元组,并支持空字段

⚫ POJO:Flink 自定义的类似于 Java bean 模式的类

        Flink 对 POJO 类型的要求如下:

  • 类是公共的(public)和独立的(standalone,也就是说没有非静态的内部类);
  • 类有一个公共的无参构造方法;
  • 类中的所有字段是 public 且非 final 的;或者有一个公共的 getter 和 setter 方法,这些方法需要符合 Java bean 的命名规范。

2.4 辅助类型

        Option、Either、List、Map 等

2.5 泛型类型(GENERIC)

        Flink 支持所有的 Java 类和 Scala 类。不过如果没有按照上面 POJO 类型的要求来定义, 就会被 Flink 当作泛型类来处理。Flink 会把泛型类型当作黑盒,无法获取它们内部的属性;它 们也不是由 Flink 本身序列化的,而是由 Kryo 序列化的。 在这些类型中,元组类型和 POJO 类型最为灵活,因为它们支持创建复杂类型。而相比之 下,POJO 还支持在键(key)的定义中直接使用字段名,这会让我们的代码可读性大大增加。 所以,在项目实践中,往往会将流处理程序中的元素类型定为 Flink 的 POJO 类型。

3.TypeExtractror 类型提取+type hints类型提示

        Flink 还具有一个类型提取系统,可以分析函数的输入和返回类型,自动获取类型信息, 从而获得对应的序列化器和反序列化器。但是,由于 Java 中泛型擦除的存在,在某些特殊情 况下(比如 Lambda 表达式中),自动提取的信息是不够精细的——只告诉 Flink 当前的元素由 “船头、船身、船尾”构成,根本无法重建出“大船”的模样;这时就需要显式地提供类型信 息,才能使应用程序正常工作或提高其性能。

        Flink 内部实现了名为 TypeExtractror 的类,可以利用方法签名、子类信息等蛛丝马迹,自动提取和恢复类型信息(当然也可以显式声明,即本文所介绍的内容)。然而由于 Java 的类型擦除,自动提取并不是总是有效。因而一些情况下(例如通过 URLClassLoader 动态加载的类),仍需手动处理;例如下图中对 DataSet 变换时,使用 .returns() 方法声明返回类型,为了解决这类问题,Java API 提供了专门的“类型提示”(type hints)。。

        这里需要说明一下,returns() 接受三种类型的参数:

  • 字符串描述的类名(例如 "String")
  • TypeHint(接下来会讲到,用于泛型类型参数)
  • Java 原生 Class(例如 String.class) 等;不过字符串形式的用法即将废弃,如果确实有必要,请使用 Class.forName() 等方法来解决。

        回忆一下之前的 word count 流处理程序,我们在将 String 类型的每个词转换成(word, count)二元组后,就明确地用 returns 指定了返回的类型。因为对于 map 里传入的 Lambda 表 达式,系统只能推断出返回的是 Tuple2 类型,而无法得到 Tuple2。只有显式地 告诉系统当前的返回类型,才能正确地解析出完整数据。

.map(word -> Tuple2.of(word, 1L))
.returns(Types.TUPLE(Types.STRING, Types.LONG));

        这是一种比较简单的场景,二元组的两个元素都是基本数据类型。那如果元组中的一个元 素又有泛型,该怎么处理呢? Flink 专门提供了 TypeHint 类,它可以捕获泛型的类型信息,并且一直记录下来,为运行 时提供足够的信息。我们同样可以通过.returns()方法,明确地指定转换之后的 DataStream 里元素的类型。

returns(new TypeHint<Tuple2<Integer, SomeType>>(){})
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值