欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~

本文由kyledong发表于云+社区专栏

使用 Flink 编写处理逻辑时,新手总是容易被林林总总的概念所混淆:

为什么 Flink 有那么多的类型声明方式?

BasicTypeInfo.STRING_TYPE_INFO、Types.STRING 、Types.STRING() 有何区别?

TypeInfoFactory 又是什么?

TypeInformation.of 和 TypeHint 是如何使用的呢?

接下来本文将逐步解密 Flink 的类型和序列化机制。

Flink 的类型分类

图 1:Flink 类型分类

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

图 2:TypeInformation 类继承关系图

可以看到,图 1 和 图 2 是一一对应的,TypeInformation 类是描述一切类型的公共基类,它和它的所有子类必须可序列化(Serializable),因为类型信息将会伴随 Flink 的作业提交,被传递给每个执行节点。

由于 Flink 自己管理内存,采用了一种非常紧凑的存储格式(见官方博文),因而类型信息在整个数据处理流程中属于至关重要的元数据。

TypeExtractror 类型提取

Flink 内部实现了名为 TypeExtractror 的类,可以利用方法签名、子类信息等蛛丝马迹,自动提取和恢复类型信息(当然也可以显式声明,即本文所介绍的内容)。

然而由于 Java 的类型擦除,自动提取并不是总是有效。因而一些情况下(例如通过 URLClassLoader 动态加载的类),仍需手动处理;例如下图中对 DataSet 变换时,使用 .returns() 方法声明返回类型。

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

图 3:使用 .returns 方法声明返回类型

下面是 ExecutionEnvironment 类的 registerType 方法,它可以向 Flink 注册子类信息(Flink 认识父类,但不一定认识子类的一些独特特性,因而需要注册),下面是 Flink-ML 机器学习库代码的例子:

图 4:Flink-ML 注册子类类型信息

从下图可以看到,如果通过 TypeExtractor.createTypeInfo(type) 方法获取到的类型信息属于 PojoTypeInfo 及其子类,那么将其注册到一起;否则统一交给 Kryo 去处理,Flink 并不过问(这种情况下性能会变差)。

图 5:Flink 允许注册自定义类型

声明类型信息的常见手段

通过 TypeInformation.of() 方法,可以简单地创建类型信息对象。

1. 对于非泛型的类,直接传入 Class 对象即可

图 6:class 对象作为参数

2. 对于泛型类,需要借助 TypeHint 来保存泛型类型信息

TypeHint 的原理是创建匿名子类,运行时 TypeExtractor 可以通过 getGenericSuperclass(). getActualTypeArguments() 方法获取保存的实际类型。

图 7:TypeHint 作为参数,保存泛型信息

3. 预定义的快捷方式

例如 BasicTypeInfo,这个类定义了一系列常用类型的快捷方式,对于 String、Boolean、Byte、Short、Integer、Long、Float、Double、Char 等基本类型的类型声明,可以直接使用。

图 8:BasicTypeInfo 快捷方式

例如下面是对 Row 类型各字段的类型声明,使用方法非常简明,不再需要 new XxxTypeInfo<>(很多很多参数)

图 9:使用 BasicTypeInfo 快捷方式来声明一行(Row)每个字段的类型信息

当然,如果觉得 BasicTypeInfo 还是太长,Flink 还提供了完全等价的 Types 类(org.apache.flink.api.common.typeinfo.Types):

图 10:Types 类

特别需要注意的是,flink-table 模块也有一个 Types 类(org.apache.flink.table.api.Types),用于 table 模块内部的类型定义信息,用法稍有不同。使用 IDE 的自动 import 时一定要小心:

图 11:flink-table 模块的 Types 类

4. 自定义 TypeInfo 和 TypeInfoFactory

通过自定义 TypeInfo 为任意类提供 Flink 原生内存管理(而非 Kryo),可令存储更紧凑,运行时也更高效。

开发者在自定义类上使用 @TypeInfo 注解,随后创建相应的 TypeInfoFactory 并覆盖 createTypeInfo 方法。

注意需要继承 TypeInformation 类,为每个字段定义类型,并覆盖元数据方法,例如是否是基本类型(isBasicType)、是否是 Tuple(isTupleType)、元数(对于一维的 Row 类型,等于字段的个数)等等,从而为 TypeExtractor 提供决策依据。

图 12:为自定义类提供类型支持(图片未展示全部字段)

更多示例,请参考 Flink 源码的 org/apache/flink/api/java/typeutils/TypeInfoFactoryTest.java

TypeSerializer

Flink 自带了很多 TypeSerializer 子类,大多数情况下各种自定义类型都是常用类型的排列组合,因而可以直接复用:

图 13:Flink 自带的 TypeSerializer 子类概览

如果不能满足,那么可以继承 TypeSerializer 及其子类以实现自己的序列化器。

Kryo 序列化

对于 Flink 无法序列化的类型(例如用户自定义类型,没有 registerType,也没有自定义 TypeInfo 和 TypeInfoFactory),默认会交给 Kryo 处理。

如果 Kryo 仍然无法处理(例如 Guava、Thrift、Protobuf 等第三方库的一些类),有以下两种解决方案:

\1. 可以强制使用 Avro 来替代 Kryo:

env.getConfig().enableForceAvro();   // env 代表 ExecutionEnvironment 对象, 下同

\2. 为 Kryo 增加自定义的 Serializer 以增强 Kryo 的功能:

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

图 14:为 Kryo 增加自定义的 Serializer

以及

env.getConfig().registerTypeWithKryoSerializer(Class<?> type, T serializer)

图 15:为 Kryo 增加自定义的 Serializer

如果希望完全禁用 Kryo(100% 使用 Flink 的序列化机制),则可以使用以下设置,但注意一切无法处理的类都将导致异常:

env.getConfig().disableGenericTypes();

类型机制的陷阱与缺陷

金无足赤,人无完人。Flink 内置的类型系统虽然强大而灵活,但仍然有一些需要注意的点:

1. Lambda 函数的类型提取

由于 Flink 类型提取依赖于继承等机制,而 lambda 函数比较特殊,它是匿名的,也没有与之相关的类,所以其类型信息较难获取。

Eclipse 的 JDT 编译器会把 lambda 函数的泛型签名等信息写入编译后的字节码中,而对于 javac 等常见的其他编译器,则不会这样做,因而 Flink 就无法获取具体类型信息了。

2. Kryo 的 JavaSerializer 在 Flink 下存在 Bug

推荐使用 org.apache.flink.api.java.typeutils.runtime.kryo.JavaSerializer 而非 com.esotericsoftware.kryo.serializers.JavaSerializer 以防止与 Flink 不兼容。

类型机制与内存管理

图 16:类型信息到内存块

下面以 StringSerializer 为例,来看下 Flink 是如何紧凑管理内存的:

图 17:StringSerializer 类的 serialize() 方法

下面是具体的序列化过程:

图 18:String 对象的序列化过程

可以看到,Flink 对于内存管理是非常细致的,层次分明,代码也容易理解。

参考阅读

Data Types & Serialization

Flink 原理与实现:内存管理

Flink 的数据类型和序列化

问答

如何使用Flink Quickstart在Eclipse IDE中缺少依赖关系?

相关阅读

Storm作业转化为Flink作业流程分析

Apache Calcite 功能简析及在 Flink 的应用

【每日课程推荐】机器学习实战!快速入门在线广告业务及CTR相应知识

此文已由作者授权腾讯云+社区发布,更多原文请点击

搜索关注公众号「云加社区」,第一时间获取技术干货,关注后回复1024 送你一份技术课程大礼包!

海量技术实践经验,尽在云加社区

作为一个编程新手,我再也不怕Flink迷了我的眼!的更多相关文章

  1. GitHub 标星 1.6w+,我发现了一个宝藏项目,作为编程新手有福了!

    大家好,我是 Rocky0429,一个最近老在 GitHub 上闲逛的蒟蒻... 特别惭愧的是,虽然我很早就知道 GitHub,但是学会逛 GitHub 的时间特别晚.当时一方面是因为菜,看着这种全是 ...

  2. 一个编程小白,如何入门APP软件开发领域?

    近些年,互联网创业火得不得了!一时间,满世界都在招做App软件开发的专业人员.从大众角度来看,学编程,写代码,是一件非常困难的事情.但是,App开发人员的工资那么诱人,让很多小白也跃跃欲试想学一下.那 ...

  3. GitHub 标星 2.4w+,最适合编程新手入门的宝藏项目推荐

      照惯例这周给大家推荐几个Github上高星的优秀项目,我的github:图灵的猫 ,也欢迎大家follow~ 下面这是第一个,也是首推的新手入门项目,以前我入门的时候如果有这样一个项目,知识广度和 ...

  4. 分享一个编程学习网站:https://github.com/justjavac/free-programming-books-zh_CN

    分享一个编程学习网站:https://github.com/justjavac/free-programming-books-zh_CN

  5. Teaching yourself programming -一个编程爱好者的碎碎念

    多数时候,个人活动的展开都源于某个具体的动机.或许你是为了可以写点小工具,解决日常生活中的一些重复劳动:或许,你心怀梦想,梦想着某一天完成一款你心目中完美的游戏:又或许是,你内心憧憬电影里的hack, ...

  6. Servlet模板,一个供新手参考的模板

    由于这学期老师的进度是刚开始教JavaSE部分,而我的进度比较快,所以买了3本javaee的书,我根据自己的基础,选择了合适的开发实践,另外两本书都和框架相关,我自认为我的web基础还不是很牢固,所以 ...

  7. 如何让一个Java新手快速入门?

    问题中问到如何让java新生快速入门,既然想快速入门的话那最简单粗暴的方法就是多看视频,加上跟着视频敲代码,刚开始可能不知道是什么意思,敲得多了就慢慢知道是什么意思了. 刚开始建议在网上找那种结合自己 ...

  8. Unix/Linux环境C编程新手教程(12) openSUSECCPP以及Linux内核驱动开发环境搭建

    1. openSUSE是一款优秀的linux. watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaXRjYXN0Y3Bw/font/5a6L5L2T/font ...

  9. Unix/Linux环境C编程新手教程(22) C/C++怎样获取程序的执行时间

    1.问:知道程序执行时间我们能够做什么? 在<C++应用程序性能优化>一书中,假设大家读过相信大家一定对性能优化这一块很上心,文中总是对优化前后的时间对照很直观给我们一个感受. 那么我们怎 ...

随机推荐

  1. Centos 7 安装记录

    0.安装中选择最小安装 1.centos7安装图形界面 之前转载过一篇“centos6安装图形界面”的文章,地址见http://my.oschina.net/u/1169607/blog/335304 ...

  2. PostgreSQL查询数据(基本查询)

    原料:数据表 create table "SysUser"( "UserId" serial, --用户Id,自增 "UserName" ) ...

  3. Xamarin.Android RelativeLayout

    初次接触Xamarin.Android. 由于国内Xamarin的资料少见,我大多参考JAVA原生代码,慢慢摸索过来. 我把摸索出来的结果广而告之,希望后来人能少走一点弯路,也希望你也能做出一份贡献. ...

  4. 【cocos2d-x 手游研发小技巧(3)Android界面分辨率适配方案】

    先感叹一下吧~~android的各种分辨率各种适配虐我千百遍,每次新项目我依旧待它如初恋···· 每家公司都有自己项目工程适配的方案,这种东西就是没有最好,只有最适合!!! 这次新项目专项针对andr ...

  5. sqlite 插入数据 too many variables

    相关文档:http://www.sqlite.org/limits.html#max_variable_number 一次插入条数限制500,参数最多999个.

  6. git删除未监视的文件

    新增的文件使用git status查看会提示Untracked files,如果想要删除Untracked files,可以使用如下命令: git clean -f # 删除Untracked fil ...

  7. 手动开发PHP模板引擎 一 (35)

    模板叫做TPL,模仿于smarty模板引擎. 我们所说的模板是Web模板,是主要由HTML标记组成的语言来编写的页面,但也有如何表示包含动态生成内容的方式(解析标签).模板引擎是一种软件库,允许我们从 ...

  8. day05.2-一个文件的增删改查实例

    一. 测试程序   #INFO.txt源文件内容 global log 127.0.1 local2 daemon maxconn 256 log 127.0.0.1 local2 info defa ...

  9. 546. Remove Boxes

    Given several boxes with different colors represented by different positive numbers. You may experie ...

  10. [USACO17FEB]Why Did the Cow Cross the Road III P(CDQ分治)

    题意 两列$n$的排列,相同的数连边,如果一对数有交叉且差的绝对值$>k$,则$++ans$,求$ans$ 题解 可以把每一个数字看成一个三元组$(x,y,z)$,其中$x$表示在第一列的位置, ...