Flink类型系统的根及基本接口

之前我们谈到了Flink通过自主管理内存的方式来,避免了让JVM管理内存带来的一些问题。自主管理内存之后,JVM中原生的类型也就不适合使用了。因此Flink也对Java的类型进行了扩展,这就是我们本节关注的内容。

本节探讨的相关类主要位于包:org.apache.flink.types

类型的根Value

Value位于所有类型的继承链的最顶端,可以说是所有类型的根。它代指所有可被序列化为Flink二进制表示的类型。该接口本身并不提供任何接口方法,但它继承自两个接口。下图是它的继承关系图:

从上图可以看出任何实现了Value接口的特定类型,都需要满足Value继承的两个接口的契约:

  • Serializable :标记实现该接口的类可被序列化
  • IOReadableWritable :Flink核心IO包种的接口,实现该接口用于将类的实例序列化为二进制的表示形式

IOReadableWritable提供了读写数据的write/read方法,另外IOReadableWritable对接口的实现者的一个要求是其必须有一个默认的(无参)构造器。

容器类型ListValue和MapValue

Value下,Flink直接提供了两个抽象的容器类型:ListValueMapValue。它们都有几个共同点:

  • 容器中存储的元素的类型都是Value类型(通过泛型类型约束)
  • 容器类型自身也实现了Value,也即自身也可被序列化
  • 都实现了JDK Java集合框架中各自的接口(ListMap

关于上面的第三点,Flink其实采用的是装饰器模式。比如,我们拿MapValue来举例:

它内部有一个map字段,该字段的初始化可能来自从构造方法传入的外部被装饰的Map实例,也可能是从无参构造方法中直接实例化的Map实例。而MapValue中实现的Map接口的方法,大都通过调用map的实例方法实现。ListValue的做法类似,不再赘述。

值得一提的是,它们对IOReadableWritableread/write方法的实现。

我们先来看一下read方法的实现:

public void read(final DataInputView in) throws IOException {
    int size = in.readInt();
    this.map.clear();

    try {
        for (; size > 0; size--) {
            final K key = this.keyClass.newInstance();
            final V val = this.valueClass.newInstance();
            key.read(in);
            val.read(in);
            this.map.put(key, val);
        }
    } catch (final InstantiationException | IllegalAccessException e) {
        throw new RuntimeException(e);
    }
}

它首先读取一个整型值size,该整型值表示的是元素对的个数。然后循环读取每个keyvalue同时反序列化之后将其加入内部的map中。

write方法的实现,则是序列化每个元素的过程:

public void write(final DataOutputView out) throws IOException {
        out.writeInt(this.map.size());
        for (final Entry<K, V> entry : this.map.entrySet()) {
            entry.getKey().write(out);
            entry.getValue().write(out);
        }
    }

当然,也是将mapsize先写入二进制结果的头部。结构示意如下图:

下面会我们来看所有具体的类型需要实现的三个接口。

基本类型实现的接口

ResettableValue接口

该接口提供了一个方法:

void setValue(T value);

用于将一个外部的value赋值给内部的同类型的对象。

CopyableValue接口

该接口提供一些拷贝方法以方便基本类型的拷贝。其类图如下:

接口方法中,值得关注的是三个copy相关的方法。前两个:copyTocopy都必须提供深拷贝的实现。而最后一个copy方法,提供在Flink的二进制表示层面上的拷贝(等价于对IOReadableWritableread以及write的先后调用,但这里copy方法的优势是,中间不需要进行反序列化的过程)。

NormalizableKey接口

该接口指定了实现规范化的键(normalizable key)需要满足的契约。先来解释一下什么叫作“规范化的键”,规范化的键指一种在二进制表示的方式下可以进行逐字节比较的键。而要使两个规范化的键能够比较,首先对于同一种类型,它们的最大字节长度要是相等的。对于这个条件,通过接口方法getMaxNormalizedKeyLen来定义。它针对一种类型通常都会返回一个常数值。比如对于32位的整型,它会返回常数值4。但一个规范化的键所占用的字节数不一定要跟该类型的最大字节数相等。当它比规定的最大的字节数小时,可以认为它只是该规范化键的一种“前缀”。

两个规范化的键进行比较,但满足两个条件的其中之一后会停止:

  • 所有的字节都比较完成
  • 两个相同位置的字节不相等

关于比较的结果,如果在相同的位置,两个字节的值不相等则值小的一个键被认为其整个键会小于另外一个键。

除此之外该接口还提供了将实现类型的值(规范化的键)写入给定的目标字节数组中去的方法。

void copyNormalizedKey(MemorySegment memory, int offset, int len);

对于该接口,值得一提的是,如果真正需要写入的字节数小于给定的len,那么它将会被填充一些特定的字符以进行补齐。

NormalizableKey接口直接继承自Key接口,Key用来使得一个类型可以作为键以建立跟值之间的关系。并且键Key要求是可被比较的,因为它实现了Comparable接口。目前Key接口被标记为“Deprecated”的,在未来的版本中可能会被废弃。


微信扫码关注公众号:Apache_Flink


QQ扫码关注QQ群:Apache Flink学习交流群(123414680)

时间: 2024-11-19 04:11:42

Flink类型系统的根及基本接口的相关文章

Go语言学习(十一)面向对象编程-类型系统

1.类型系统介绍 对于面向对象编程的支持Go 语言设计得非常简洁而优雅.简洁之处在于,Go语言并没有沿 袭传统面向对象编程中的诸多概念,比如继承.虚函数.构造函数和析构函数.隐藏的 this 指 针等. 一个典型的类型系统通常包含如下基本内容: 基础类型,如 byte . int . bool . float 等: 复合类型,如数组.结构体.指针等: 可以指向任意对象的类型( Any 类型): 面向对象,即所有具备面向对象特征(比如成员方法)的类型: 接口. 因为Java语言自诞生以来被称为最纯

Apache Flink fault tolerance源码剖析(一)

因某些童鞋的建议,从这篇文章开始结合源码谈谈Flink Fault Tolerance相关的话题.上篇官方介绍的翻译是理解这个话题的前提,所以如果你想更深入得了解Flink Fault Tolerance的机制,推荐先读一下前篇文章理解它的实现原理.当然原理归原理,原理体现在代码实现里并不是想象中的那么直观.这里的源码剖析也是我学习以及理解的过程. 作为源码解析Flink Fault Tolerance的首篇文章,我们先暂且不谈太有深度的东西,先来了解一下:Flink哪里涉及到检查点/快照机制来

Flink运行时之流处理程序生成流图

流处理程序生成流图 DataStream API所编写的流处理应用程序在生成作业图(JobGraph)并提交给JobManager之前,会预先生成流图(StreamGraph). 什么是流图 流图(StreamGraph)是表示流处理程序拓扑的数据结构,它封装了生成作业图(JobGraph)的必要信息.它的类继承关系如下图所示: 当你基于StreamGraph的继承链向上追溯,会发现它实现了FlinkPlan接口. Flink效仿了传统的关系型数据库在执行SQL时生成执行计划并对其进行优化的思路

JavaScript大杂烩1 - 理解JavaScript的类型系统

随着硬件水平的逐渐提高,浏览器的处理能力越来越强大,本人坚信,客户端会越来越瘦,瘦到只用浏览器就够了,服务端会越来越丰满:虽然很多大型的程序,比如3D软件,客户端仍然会存在,但是未来的主流必将是浏览器,也就是Web程序/网站. Web前端开发模式:Thinking in "DIV + CSS + JS (JavaScript)" 任何面向用户的程序,最终都表现为3个部分:界面,逻辑,数据.而经过几十年的编程实践,大家都发现,当把这3个部分以弱耦合的形式结合起来的时候,开发的灵活性和效率

生成树失败,结果路由器接口都堵死了

在R2.R3.S2.S3配了ospf路由通了,路由表也学习到了,然后去S1.S2.S3配生成树,结果所有接口都堵死了,包括路由器的串口.生成树选举出根桥后接口又重新通了,所以是不是生成树影响到了路由器?还有这个拓补里怎么形成的环路?

Flink执行时之流处理程序生成流图

流处理程序生成流图 DataStream API所编写的流处理应用程序在生成作业图(JobGraph)并提交给JobManager之前,会预先生成流图(StreamGraph). 什么是流图 流图(StreamGraph)是表示流处理程序拓扑的数据结构.它封装了生成作业图(JobGraph)的必要信息.它的类继承关系例如以下图所看到的: 当你基于StreamGraph的继承链向上追溯,会发现它实现了FlinkPlan接口. Flink效仿了传统的关系型数据库在运行SQL时生成运行计划并对其进行优

Servlet请求转发RequestDispatcher接口

在Servlet中,利用RequestDispatcher对象,可以将请求转发给另外一个Servlet或JSP页面,甚至是HTML页面,来处理对请求的响应. 一,RequestDispatcher接口方法简介 1,RequestDispatcher对象由Servlet容器来创建,封装一个由路径所标识的服务器资源. 2,RequestDispatcher接口中定义了二种方法用于请求转发: forward(ServletRequest,ServletResponse)方法: 将请求转发给服务器上另外

flink与hbase交互

1. HBase连接的方式概况 主要分为: 纯Java API读写HBase的方式: Spark读写HBase的方式: Flink读写HBase的方式: HBase通过Phoenix读写的方式: 第一种方式是HBase自身提供的比较原始的高效操作方式,而第二.第三则分别是Spark.Flink集成HBase的方式,最后一种是第三方插件Phoenix集成的JDBC方式,Phoenix集成的JDBC操作方式也能在Spark.Flink中调用. 注意: 这里我们使用HBase2.1.2版本,flink

Scala中的空

Scala的有即Any,Scala的无是Null,null,Nil,Nothing,None,Unit.那么这几种空有什么区别呢? 一.Null&null 很多人一辈子都没有走出这个无.Null是一个Trait,你不能创建她它的实例.但是Scala在语言层面上存在一个Null的实例,那就是null.Java中的null意味着引用并没有指向任何对象.但存在一个悖论,一切都是对象,那没有对象是不是也是对象呢?Scala定义了一个类似于对象语义的Null,和一个值语义的null.这样面向对象在空引用的