一、Flink 常用API说明
Flink根据抽象程度分层,提供了3种不同的API和库,每一种API在简洁性和表达力上有着不同的侧重,并且针对不同的应用场景。
1、ProcessFunctinon
ProcessFunctinon是Flink所提供最底层接口。ProcessFunction可以处理一或两条输入数据流中的单个事件或者归入一个特定窗口内的多个事件,它提供了对于时间和状态的细粒度控制,开发者可以在其中任意地修改状态,也能够注册定时器用以在未来的某一时刻触发回调函数.因此你可以利用ProcessFunctino实现许多有状态的事件驱动应用所需要的基于单个事件的复杂业务逻辑。
2、DataStream API
为许多通用的流处理操作提供了处理原语
这些操作包括窗口、逐条记录的的转换操作,在处理时间时进行外部数据库查询等。 DataStream API支持Java 和 Scala语言,预先定义了例如map() 、reduce() 、Aggregate()等函数,你可以通过扩展实现预定义接口或使用Java、SCala的lambda表达式实现自定义的函数。
3、SQL& Table API
Flink支持两种关系型的API,Table API和SQL,这两个API都是批处理,和流处理统一的API,这意味着在无边界的实时数据流和有边界的历史记录数据流上,关系型API 会以相同的语义执行查询,并产生相同的结果。Table API 和SQL 接住了 Apache Calcite来进行查询的解析,校验以及优化,它们可以与DataStream和DataSEt API无缝集成,并支持用户自定义的标量函数,聚合函数以及标志函数。
4、复杂事件处理-CEP
模式检测是事件流处理中的一个非常常见的用例. Flink 的CEP 库提供了API,使用户能够以例如正则表达式或状态机的方式指定事件模式,CEP库与Flink的DataStream API集成,以便在DataStream上评估模式。
CEP库的应用包括网络入侵检测,业务流程监控和欺诈检测。
5、DataSet API
DataSet API 是Flink用于批处理应用程序的核心 API, DataSet API所提供的的基础算子 包括 map、reduce、(outer) join、co-group、iterate等。
所有算子都有相应的算法和数据结构支持,对内存中的序列化数据进行操作。如果数据大小超过预留内存,则过量数据将存储到磁盘。
二、Gallery 可扩展的图形处理和分析库
1、DataStream 的编程模型
DataStream的编程模型包括四个部分: Environment,DataSource,Trasnformation,Sink
四部分的所处的环节如下:
Environment(初始化上下文环境) -> DataSource(数据源) -> Trasnformation(转换操作) -> Sink(数据输出)
2、Flink的DataSource数据源
Flink已经封装实现了较多的数据来源,只要调用对应的API即可,如下讲解几个常用的数据源实现方式:
读取HDFS数据源实现方式:env.readTextFile($HDFS_PATH),需要实现的maven依赖为:
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.7.2</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.7.2</version>
</dependency>
基于集合的Source 实现:env.fromCollection()
3、Flink的Sink数据目标
Flink针对DataStream提供了大量的已经实现的数据目标(Sink),包括文件,Kafka,Redis,HDFS,Elasticsearch
1. 基于HDFS的Sink
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-connector-filesystem_2.11</artifactId>
<version>1.9.1</version>
</dependency>
Streaming File Sink能把数据写入HDFS中,还可以支持分桶写入,每一个分桶就对应HDFS中的一个目录,
默认按照该小时来分桶,在一个桶内部,会进一步将输出基于滚动策略切分成更小的文件.这有助于防止桶文件变得过大,
滚动策略也是可以配置的,默认策略会更具文件大小和超时时间来滚动文件,超时时间是指没有新数据写入部分文件(part file)的时间
2. 基于Redis的Sink
3. 基于Kafka的Sink
4. 自定义的Sink
4、DataStream 转换算子
即通过从一个或多个DataStream生成新的DataStream的过程被称为Transformation操作. 在转换过程中,每种操作类型被定义为不同的Operator,Flink程序都能够将多个Transformation组成一个DataFlow的拓扑。
- Map[DataStream->DataStream]
调用用户自定义的MapFunction对DataStream[T]数据进行处理,形成新的DataStream[T],其中数据格式可能会发生变化,常用作对数据集内数据的清洗和转换,例如将输入数据集中的每个数值全部加1处理,并且将数据输出到下游数据集。- FlatMap[DataStream->DataStream]
该算子主要应用处理输入一个元素产生一个或者多个元素的计算场景,比较常见的是在经典例子WordCount中,将每一行的文本数据切割,生成单词序列。- Filter[DataStream->DataStream]
该算子将按照条件对输入数据集进行筛选操作,将符合条件的数据集输出,将不符合条件的数据过滤掉。
3.1 通过通配符
DataStream<int> filter = dataStream.filter{_%2==0}
3.2 或者指定运算表达式
DataStream<int> filter = dataStream.filter(x -> x%2==0)- KeyBy[DataStream->KeyedStream]
该算子根据指定的key将输入的DataStream[T]数据格式转换为KeyedStream[T],也就是在数据集中执行Partition操作,将相同的Key值的数据放置在相同的分区中。
例如WordCount-> 将数据集中第一个参数作为Key,对数据集进行KeyBy函数操作,形成根据Id分区的KeyedStream数据集。
eg:
val dataStream = env.fromElements((1,5),(2,2),(2,4),(1,3))
//指定第一个字段为分区key
val keyedStream: KeyedStream [(String,Int),Tuple] = dataStream.keyBy(0)- Reduce[KeyedStream->DataStream]
该算子和MapReduce中Reduce原理基本一致,主要目的是将输入的KeyedStream通过传入的用户自定义地ReduceFunction滚动地进行数据聚合处理,其中定义ReduceFunction必须满足运算结合律和交换律,
eg:
对传入的KeyedStream数据集中相同key值的数据独立进行求和运算,得到每个key所对应的求和值。
val dataStream = env.fromElements((“a”,3),(“d”,4),(“c”,2),(“a”,5))
//指定第一个字段为分区key`
val keyedStream:KeyedStream[(String,Int),Tuple] = dataStream.keyBy(0)
// 滚动对第二个字段进行reduce相加求和
val reduceStream = keyedStream.reduce{(x1,x2)=>(x1._1,x1._2+x2._2)- Aggregations[KeyedStream->DataStream]
Aggregations是KeyedDataStream接口提供的聚合算子,根据指定的字段进行聚合操作,滚动地产生一系列数据聚合结果。
其实是将Reduce算子中的函数进行了封装,封装的聚合操作有sum、min、minBy、max、maxBy等,这样就不需要用户自己定义Reduce函数。
eg:
指定数据集中第一个字段作为key,用第二个字段作为累加字段,然后滚动地对第二个字段的数值进行累加并输出
//指定第一个字段为分区key
val keyedStream:KeyedStream[(Int,Int),Tuple] = dataStream.keyBy(0)
// 对对第二个字段进行sum统计
val sumStream:DataStream[(Int,Int)] = keyedStream.sum(1)
// 输出计算结果
sumStream.print()- Union[DataStream->DataStream]
union算子主要是将两个或者多个输入的数据集合并成一个数据集,需要保证两个数据集的格式一致,输出的数据集的格式和输入的数据集格式保持一致,
code:
//获取flink实时流处理的环境
val env = ExecutionEnvironment.getExecutionEnvironment
// 创建不同的数据集
val dataStream = env.fromElements((“a”, 3), (“d”, 4), (“c”, 2), (“a”, 5))
val dataStream2 = env.fromElements((“a”, 1), (“d”, 1), (“c”, 1), (“a”, 1))
// 调用union算子进行不同的数据集合并
dataStream.union(dataStream2).print()- Connect,CoMap,CoFlatMap[DataStream->ConnectedStream->DataStream](只能在Stream才可以用)
connect算子主要是为了合并两种或者多种不同数据类型的数据集,合并后会保留原来原来数据集的数据类型。
例如: dataStream1数据集为(String,Int) 元组类型,dataStream2数据集为Int类型,通过connect连接算子将两个不同数据类型的流结合在一起,形成格式为ConnectedStreams的数据集,其内部数据为[(String,Int),Int]的混合数据类型,保留了两个原始数据集的数据类型
eg:
//获取flink实时流处理的环境
val env = StreamExecutionEnvironment.getExecutionEnvironment
// 创建不同的数据集
val dataStream = env.fromElements((“a”, 3), (“d”, 4), (“c”, 2), (“a”, 5))
val dataStream2 = env.fromElements(1, 2, 4, 5)
// 连接两个DataStream数据集
val connectedStream = dataStream.connect(dataStream2)
val result = connectedStream.map(
//第一个处理函数
t1 => {
(t1._1, t1._2)
},
//第二个处理函数
t2 => {
(t2, 0)
})
result.print()
env.execute(“h”)
注意:Union和Connect区别
1. Union之间两个流的类型必须是一样,Connect可以不一样,在之后的coMap中在去调整成为一样的.
2. Connect只能操作两个流,Union可以操作多个- Split 和 select [DataStream -> SplitStream->DataStream]
Split算子是将一个DataStream数据集按照条件进行拆分,形成两个数据集的过程,也是Union算子的逆向实现,每个接入的数据都会被路由到一个或者多个输出数据集中,在使用Splict函数中,需要定义split函数中的切分逻辑,通过调用split函数,然后指定条件判断函数。
例如: 如下代码所示,将根据第二个字段的奇偶性将数据集标记出来,如果是偶数则标记为even,如果是奇数则标记为odd,然后通过集合将标记返回,最终生成格式SplitStream的数据集
code:
//获取flink实时流处理的环境
val env = StreamExecutionEnvironment.getExecutionEnvironment
// 导入Flink隐式转换
import org.apache.flink.streaming.api.scala._
// 创建不同的数据集
val dataStream = env.fromElements((“a”, 3), (“d”, 4), (“c”, 2), (“a”, 5))
val splitedStream = dataStream.split(t => if (t._2 % 2 == 0) Seq(“even”) else Seq(“odd”))
// Split函数本身只是对输入数据集进行标记,并没有将数据集真正的实现拆分,因此需要借助Select函数根据标记将数据切分成不同的数据集,
//筛选出偶数数据集
val evenStream = splitedStream.select(“even”).print()
//筛选出偶数数据集
val oddStream = splitedStream.select(“odd”)
env.execute("l ")
5、函数类和富函数类
前面学过的所有算子集合都可以自定义一个函数类,富函数类作为参数,因为Flink暴露了这两种函数类的接口,常见的函数接口:
1. MapFunction
2. FlatMapFunction
3. ReduceFunction
富函数接口它与其他常规函数接口的不同在于:可以获取运行环境的上下文,在上下文环境中可以管理状态(State),并拥有一些生命周期方法,
所以可以实现更复杂的功能.富函数的接口有:
1. RichMapFunction
2. RichFlatMapFunction
3. RichFilterFunction
6、输出流 Side Output
在Flink处理数据流时,我们经常会遇到这样的情况: 在处理一个数据源时,往往需要将该源中的不同类型的数据做分割处理,如果使用filter算子对数据源进行筛选分割的话,势必会造成数据流的多次复制,造成不必要的性能浪费;flink中的侧输出就是将数据流进行分割,而不对流进行复制的一种分流机制,Flink的侧输出的另一个作用就是对延时迟到的数据进行处理,这样就可以不必丢弃迟到的数据。