Flink编程模型
数据集类型
有界数据集
具有时间边界,处理过程中数据一定会在某个时间范围内起始和结束,对有界数据集的数据处理方式被称为批
计算(Batch Processing)。
无界数据集
没有边界,对无界数据
集的数据处理方式被称为流式数据处理,简称为流处理(Streaming Process)
Flink编程接口
Flink根据数据集类型的不同将核心数据处理接口分为两大类,一类是支持批计算的接口DataSet API,另外一类是支持流计算的接口DataStream API。同时Flink将数据处理接口抽象成四层,由上向下分别为SQL API、Table API、DataStream /DataSet API以及Stateful Stream Processing API,用户可以根据需要选择任意一层抽象接口来开发
Flink应用。
Flink SQL
Flink的SQL语句可以参考阿里云实时计算Flink SQL概述(Flink版本1.9)
Flink Table API
Table API将内存中的DataStream和DataSet数据集在原有的基础之上增加Schema信息,将数据类型统一抽象成表结构,然后通过Table API提供的接口处理对应的数据集。pyflink这个库就是针对这一层开发的。
DataStream API和DataSet API
Stateful Stream Process API
Flink程序结构
Execution Environment
运行Flink程序的第一步就是获取相应的执行环境,执行环境决定了程序执行在什么环境(例如本地运行环境或者集群运行环境)中。
初始化数据
创建完成ExecutionEnvironment后,需要将数据引入到Flink系统中。可以从文件中,或者其他地方直接读,例如:kafka
执行转换操作
数据从外部系统读取并转换成DataStream或者DataSet数据集后,下一步就将对数据集进行各种转换操作。Flink中的Transformation操作都是通过不同的Operator来实现,每个Operator内部通过实现Function接口完成数据处理逻辑的定义。
分区Key指定
在DataStream数据经过不同的算子转换过程中,某些算子需要根据指定的key进行转换,常见的有join、coGroup、groupBy类算子,需要先将DataStream或DataSet数据集转换成对应的KeyedStream和GroupedDataSet,主要目的是将相同key值的数据路由到相同的Pipeline中,然后进行下一步的计算操作。需要注意的是,在Flink中这种操作并不是真正意义上将数据集转换成Key-Value结构,而是一种虚拟的key,目的仅仅是帮助后面的基于Key的算子使用,分区人Key可以通过两种方式指定:
根据字段位置指定
在DataStream API中通过keyBy()方法将DataStream数据集根据指定的key转换成重新分区的KeyedStream。keyby中传入的值为数据的下标。
根据字段名称指定
通过Key选择器指定
输出结果
数据集经过转换操作之后,形成最终的结果数据集,一般需要将数据集输出在外部系统中或者输出在控制台之上。Flink在系统中定义了大量的Connector,方便用户和外部系统交互,用户可以直接通过调用addSink()添加输出系统定义的DataSink类算子,这样就能将数据输出到外部系统。
程序触发
所有的计算逻辑全部操作定义好之后,需要调用ExecutionEnvironment的execute()方法来触发应用程序的执行,其中execute()方法返回的结果类型为JobExecutionResult,里面包含了程序执行的时间和累加器等指标。需要注意的是,execute方法调用会因为应用的类型有所不同,DataStream流式应用需要显性地指定execute()方法运行程序,如果不调用则Flink流式程序不会执行,但对于DataSet API输出算子中已经包含对execute()方法的调用,则不需要显性调用execute()方法,否则会出现程序异常。
DataStream API介绍与使用
DataStream编程模型
DataStream API主要可为分为三个部分,DataSource模块、Transformation模块以及DataSink模块,其中Sources模块主要定义了数据接入功能,主要是将各种外部数据接入至Flink系统中,并将接入数据转换成对应的DataStream数据集。在Transformation模块定义了对DataStream数据集的各种转换操作,例如进行map、filter、windows等操作。最后,将结果数据通过DataSink模块写出到外部存储介质中,例如将数据输出到文件或Kafka消息中间件等。
DataSources数据输入
Flink数据源:
- 内置数据源
- 文件
- Socket网络端口
- 集合类型数据
- 第三方数据源
- Flink中定义了丰富的第三方数据源连接器
- kafka
- Elatic Search
- Flink中定义了丰富的第三方数据源连接器
也可以自定义实现Flink中数据接入函数SourceFunction,并封装成第三方数据源的Connector,完成Flink与其他外部系统的数据交互。
内置数据源
- 文件数据源
Flink系统支持将文件内容读取到系统中,并转换成分布式数据集DataStream进行数据处理。在StreamExecutionEnvironment中,可以使用readTextFile方法直接读取文本文件,
在DataStream API中,可以在readFile方法中指定文件读取类型(WatchType)、检测文件变换时间间隔(interval)、文件路径过滤条件(FilePathFilter)等参数,其中WatchType共分为两种模式——PROCESS_CONTINUOUSLY和PROCESS_ONCE模式。在PROCESS_CONTINUOUSLY模式下,一旦检测到文件内容发生变化,Flink会将该文件全部内容加载到Flink系统中进行处理。而在PROCESS_ONCE模式下,当文件内容发生变化
时,只会将变化的数据读取至Flink中,在这种情况下数据只会被读取和处理一次。
- Socket数据源
Flink支持从Socket端口中接入数据,在StreamExecutionEnvironment调用socket-TextStream方法。 该方法的参数是ip地址和端口
_集合数据源_Flink可以直接将Java或Scala程序中集合类(Collection)转换成DataStream数据集
外部数据源
Flink通过实现SourceFunction定义了非常丰富的第三方数据连接器,基本覆盖了大部分的
高性能存储介质以及中间件等,其中部分连接器是仅支持读取数据,例如Twitter Streaming API、Netty等;另外一部分仅支持数据输出(Sink),不支持数据输入(Source),例如Apache Cassandra、Elasticsearch、Hadoop FileSystem等。还有一部分是既支持数据输入,也支持数据输出,例如Apache Kafka、Amazon Kinesis、RabbitMQ等连接器。
- 自定义数据源连接器
DataSoures定义完成后,可以通过使用SteamExecutionEnvironment的addSources方法添加数据源。
DataStream转换操作
通过从一个或多个DataStream生成新的DataStream的过程被称为Transformation操作。在转换过程中,每种操作类型被定义为不同的Operator,Flink程序能够将多个Transformation组成一个DataFlow的拓扑。所有DataStream的转换操作可分为单Single-DataStream、Multi-DaataStream、物理分区三类类型。其中Single-DataStream操作定义了
对单个DataStream数据集元素的处理逻辑,Multi-DataStream操作定义了对多个DataStream数据集元素的处理逻辑。物理分区定义了对数据集中的并行度和数据分区调整转换的处理逻辑。
Single-DataStream操作
- Map [DataStream->DataStream]
调用用户定义的MapFunction对DataStream[T]数据进行处理,形成新的Data-Stream[T],其中数据格式可能会发生变化,常用作对数据集内数据的清洗和转换。
- FlatMap [DataStream->DataStream]
该算子主要应用处理输入一个元素产生一个或者多个元素的计算场景
- Filter [DataStream->DataStream]
该算子将按照条件对输入数据集进行筛选操作,将符合条件的数据集输出,将不符合条件的数据过滤掉。
- KeyBy [DataStream->KeyedStream]
算子根据指定的Key将输入的DataStream[T]数据格式转换为KeyedStream[T],也就是在数据集中执行Partition操作,将相同的Key值的数据放置在相同的分区中。
Reduce [KeyedStream->DataStream]
该算子和MapReduce中Reduce原理基本一致,主要目的是将输入的KeyedStream通过传入的用户自定义的ReduceFunction滚动地进行数据聚合处理,其中定义的ReduceFunciton必须满足运算结合律和交换律。
Aggregations[KeyedStream->DataStream]
Aggregations是DataStream接口提供的聚合算子,根据指定的字段进行聚合操作,滚动地产生一系列数据聚合结果。其实是将Reduce算子中的函数进行了封装,封装的聚合操作有sum、min、minBy、max、maxBy等,这样就不需要用户自己定义Reduce函数。
Multi-DataStream操作
- Union[DataStream ->DataStream]
Union算子主要是将两个或者多个输入的数据集合并成一个数据集,需要保证两个数据集的格式一致,输出的数据集的格式和输入的数据集格式保持一致。
- Connect,CoMap,CoFlatMap[DataStream ->DataStream]
Connect算子主要是为了合并两种或者多种不同数据类型的数据集,合并后会保留原来数据集的数据类型。连接操作允许共享状态数据没懂
- Split [DataStream->SplitStream]
Split算子是将一个DataStream数据集按照条件进行拆分,形成两个数据集的过程,也是union算子的逆向实现。
- Select [SplitStream ->DataStream]
split函数本身只是对输入数据集进行标记,并没有将数据集真正的实现切分,因此需要借助Select函数根据标记将数据切分成不同的数据集。
- Iterate[DataStream->IterativeStream->DataStream]
Iterate算子适合于迭代计算场景,通过每一次的迭代计算,并将计算结果反馈到下一次迭代计算中。
物理分区
DataSinks数据输出
在Flink中将DataStream数据输出到外部系统的过程被定义为DataSink操作。
基本数据输出
基本数据输出包含了文件输出、客户端输出、Socket网络端口等
时间概念与watermark
时间概念类型
对于流式数据处理,最大的特点是数据上具有时间的属性特征,Flimk根据时间产生的位置不同,将时间区分为三种时间概念,分别为事件生成时间(Event Time)、事件接入时间(Ingestion Time)和事件处理时间(Processing Time)。数据从终端产生,或者从系统中产生的过程中生成的时间为事件生成时间,当数据经过消息中间件传入到Flink系统中,在DataSource中接入的时候会生成事件接入时间,当数据在Flink系统中通过各个算子实例执行转换操作的过程中,算子实例所在系统的时间为数据处理时间。
事件时间
事件时间(Event Time)是每个独立事件在产生它的设备上发生的时间,这个时间通常在事件进入Flink之前就已经嵌入到事件中,时间顺序取决于事件产生的地方,和下游数据处理系统的时间无关。事件数据具有不变的事件时间属性,该时间自事件元素产生就不会改变。在Flink SQL中事件时间目前只支持TIMESTAMP类型。
接入时间
接入时间(Ingestion Time)是数据进入Flink系统的时间,Ingestion Time依赖于Source Operator所在主机的系统时钟。
处理时间
处理时间(Processing Time)是指数据在操作算子计算过程中获取到的所在主机时间。当用户选择使用Processing Time时,所有和时间相关的计算算子,例如Windows计算,在当前的任务中所有的算子将直接使用其所在主机的系统时间。
时间概念指定
在Flink中默认情况下使用是Process Time时间概念,如果用户选择使用Event Time或者Ingestion Time概念,则需要在创建的StreamExecutionEnvironment中调用setStream-TimeCharacteristic()方法设定系统的时间概念,如下代码使用TimeCharacteristic.EventTime作为系统的时间概念,这样对当前的StreamExecutionEnvironment会全局生效。
EventTime和Watermark
什么是Watermark
指定Timestamps和生成Watermarks
如果使用Event Time时间概念处理流式数据,除了在StreamExecationEviromment中指定TimeCharacteristic外,还需要在Flink程序中指定Event Time时间戳在数据中的字段信息,在Flink程序运行过程中会通过指定字段抽取出对应的事件时间,该过程叫作Timestamps Assigning。简单来讲,就是告诉系统需要用哪个字段作为事件时间的数据来
源。
Windows窗口计算
Windows计算是流式计算中非常常用的数据计算方式之一,通过按照固定时间或长度将数据流切分成不同的窗口,然后对数据进行相应的聚合运算,从而得到一定时间范围内的统计结果。
Flink DataStream API将窗口抽象成独立的Operator,且在Flink DataStream API中已经內建了大多数窗口算子。在每个窗口算子中包含了Windows Assigner、Windows Trigger(窗口触发器)、Evictor(数据剔除器)、
Lateness(时延设定)、Output Tag(输出标签)以及Windows Funciton等组成部分,其中Windows Assigner和Windows Funciton是所有窗口算子必须指定的属性,其余的属性都是根据实际情况选择指定。
- Windows Assigner:指定窗口的类型,定义如何将数据流分配到一个或多个窗口;
- Windows Trigger:指定窗口触发的时机,定义窗口满足什么样的条件触发计算;
- Evictor:用于数据剔除;
- Lateness:标记是否处理迟到数据,当迟到数据到达窗口中是否触发计算;
- Output Tag:标记输出标签,然后在通过getSideOutput将窗口中的数据根据标签输
出;
- Windows Funciton:定义窗口上数据处理的逻辑,例如对数据进行sum操作。
Windows Assigner
Flink支持两种类型的窗口,一种是基于时间的窗口,窗口基于起始时间戳(闭区间)和终止时间戳(开区间)来决定窗口的大小,数据根据时间戳被分配到不同的窗口中完成计算。另一种是基于数量的窗口,根据固定的数量定义窗口的大小,例如每5000条数据形成一个窗口,窗口中接入的数据依赖于数据接入到算子中的顺序,如果数据出现乱序情况,将导致窗口的计算结果不确定。
在Flink流式计算中,通过Windows Assigner将接入数据分配到不同的窗口,根据Windows Assigner数据分配方式的不同将Windows分为4大类,分别是滚动窗口(TumblingWindows)、滑动窗口(Sliding Windows)、会话窗口(Session Windows)和全局窗口(Global Windows)。滚动窗口
滚动窗口是根据固定时间或大小进行切分,且窗口和窗口之间的元素互不重叠。滑动窗口
在滚动窗口基础之上增加了窗口滑动时间(Slide Time),且允许窗口数据发生重叠。会话窗口
会话窗口(Session Windows)主要是将某段时间内活跃度较高的数据聚合成一个窗口进行计算,窗口的触发的条件是Session Gap,是指在规定的时间内如果没有数据活跃接入,则认为窗口结束,然后触发窗口计算结果。全局窗口
全局窗口(Global Windows)将所有相同的key的数据分配到单个窗口中计算结果,窗口没有起始和结束时间,窗口需要借助于Triger来触发计算,如果不对Global Windows指定Triger,窗口是不会触发计算的。