flink的知识总结
flink的编程模型分为 四部分:enviroment--->source---->transformation--->sink
1.flink的数据源
1.文件数据源
//初始化Flink的Streaming(流计算)上下文执行环境
val streamEnv = StreamExecutionEnvironment.getExecutionEnvironment
//读取数据
val stream = streamEnv.readTextFile("hdfs://hadoop101:9000/wc.txt")
2.集合数据源
//读取数据 里面放的是集合
var dataStream =streamEnv.fromCollection()
3.kafka的数据源
val props = new Properties()
props.setProperty("bootstrap.servers","hadoop101:9092,hadoop102:9092,hadoop10 3:9092")
props.setProperty("group.id","fink01")
props.setProperty("key.deserializer",classOf[StringDeserializer].getName)
props.setProperty("value.deserializer",classOf[StringDeserializer].getName)
props.setProperty("auto.offset.reset","latest")
//设置kafka为数据源
val stream = streamEnv.addSource(new FlinkKafkaConsumer[String]("t_topic",new SimpleStringSchema(),props))
stream.print()
streamEnv.execute()
4.自定义数据源
有两种方式实现:
通过实现 SourceFunction 接口来自定义无并行度(也就是并行度只能为 1)的 Source。
通过实现 ParallelSourceFunction 接口或者继承 RichParallelSourceFunction 来自定义有并行度的数据源。
2.flink的sink数据目标
sink有很多简单列出几个
1.基于hdfs的sink
object HDFSFileSink {
def main(args: Array[String]): Unit = {
//初始化Flink的Streaming(流计算)上下文执行环境
val streamEnv = StreamExecutionEnvironment.getExecutionEnvironment streamEnv.setParallelism(1)
val data: DataStream[StationLog] = streamEnv.addSource("----")
//创建一个HDFS Sink
var hdfsSink =StreamingFileSink.forRowFormat[StationLog]( new Path("hdfs://hadoop101:9000/sink001/"),
new SimpleStringEncoder[StationLog]("UTF-8"))
.withBucketCheckInterval(1000) //检查分桶的间隔时间
.withRollingPolicy(rolling)
.build() data.addSink(hdfsSink)
streamEnv.execute()
}
}
2.基于redis的sink
3.基于kafka的sink
4.自定义sink
3.dataStream的转换算子:
1,Map [DataStream->DataStream]
调 用 用 户 定 义 的 MapFunction 对 DataStream[T] 数 据 进 行 处 理 , 形 成 新 的Data-Stream[T],其中数据格式可能会发生变化,常用作对数据集内数据的清洗和转换。
2.FlatMap [DataStream->DataStream]
该算子主要应用处理输入一个元素产生一个或者多个元素的计算场景,比较常见的是在 经典例子 WordCount 中,将每一行的文本数据切割,生成单词序列如在图所示,对于输入DataStream[String]通过 FlatMap 函数进行处理,字符串数字按逗号切割,然后形成新的整数数据集。
3.Filter [DataStream->DataStream]
该算子将按照条件对输入数据集进行筛选操作,将符合条件的数据集输出,将不符合条 件的数据过滤掉。如下图所示将输入数据集中偶数过滤出来,奇数从数据集中去除。
//通过通配符 val filter:DataStream[Int] = dataStream.filter { _ % 2 == 0 }
//或者指定运算表达式 val filter:DataStream[Int] = dataStream.filter { x => x % 2 == 0 } |
4.KeyBy [DataStream->KeyedStream]
该算子根据指定的 Key 将输入的 DataStream[T]数据格式转换为 KeyedStream[T],也就是在数据集中执行 Partition 操作,将相同的 Key 值的数据放置在相同的分区中。
5.Reduce [KeyedStream->DataStream]
该算子和 MapReduce 中 Reduce 原理基本一致,主要目的是将输入的 KeyedStream 通过传入的用户自定义的 ReduceFunction 滚动地进行数据聚合处理, 其中定义的ReduceFunciton 必须满足运算结合律和交换律。如下代码对传入 keyedStream 数据集中相同的 key 值的数据独立进行求和运算,得到每个 key 所对应的求和值。
val dataStream = env.fromElements(("a", 3), ("d", 4), ("c", 2), ("c",5), ("a", 5)) //指定第一个字段为分区Key val keyedStream: KeyedStream[(String,Int), Tuple] = dataStream.keyBy(0) /滚动对第二个字段进行reduce相加求和 val reduceStream = keyedStream.reduce { (t1, t2) => (t1._1, t1._2 + t2._2) |
6.Aggregations[KeyedStream->DataStream]
Aggregations 是 KeyedDataStream 接口提供的聚合算子,根据指定的字段进行聚合操作,滚动地产生一系列数据聚合结果。其实是将 Reduce 算子中的函数进行了封装,封装的聚合操作有 等,这样就不需要用户自己定义 Reduce 函数。如下代码所示,指定数据集中第一个字段作为 key,用第二个字段作为累加字段,然后滚动地对第二个字段的数值进行累加并输出。
/指定第一个字段为分区Key val keyedStream: KeyedStream[(Int, Int), Tuple] = dataStream.keyBy(0) //对第二个字段进行sum统计 val sumStream: DataStream[(Int, Int)] = keyedStream.sum(1) //输出计算结果sumStream.print() |
7.Union[DataStream ->DataStream]
Union 算子主要是将两个或者多个输入的数据集合并成一个数据集,需要保证两个数据集的格式一致,输出的数据集的格式和输入的数据集格式保持一致,如图所示,将灰色方块 数据集和黑色方块数据集合并成一个大的数据集。
//创建不同的数据集 val dataStream1: DataStream[(String, Int)] = env.fromElements(("a", 3), ("d", 4), ("c", 2), ("c", 5), ("a", 5)) val dataStream2: DataStream[(String, Int)] = env.fromElements(("d", 1), ("s", 2), ("a", 4), ("e", 5), ("a", 6)) val dataStream3: DataStream[(String, Int)] = env.fromElements(("a", 2), ("d", 1), ("s", 2), ("c", 3), ("b", 1)) //合并两个DataStream数据集 val unionStream = dataStream1.union(dataStream_02) //合并多个DataStream数据集 val allUnionStream = dataStream1.union(dataStream2, dataStream3) |
8.Connect,CoMap,CoFlatMap[DataStream ->ConnectedStream->DataStream]
Connect 算子主要是为了合并两种或者多种不同数据类型的数据集,合并后会保留原来数据集的数据类型。例如:dataStream1 数据集为(String, Int)元祖类型,dataStream2 数据集为 Int 类型,通过 connect 连接算子将两个不同数据类型的流结合在一起,形成格式为 ConnectedStreams 的数据集,其内部数据为[(String, Int), Int]的混合数据类型,保留了两个原始数据集的数据类型。
//创建不同数据类型的数据集 val dataStream1: DataStream[(String, Int)] = env.fromElements(("a", 3), ("d", 4), ("c", 2), ("c", 5), ("a", 5)) val dataStream2: DataStream[Int] = env.fromElements(1, 2, 4, 5, 6) //连接两个DataStream数据集 val connectedStream: ConnectedStreams[(String, Int), Int] = dataStream1.connect(dataStream2) |
需要注意的是,对于 ConnectedStreams 类型的数据集不能直接进行类似 Print()的操作,需要再转换成 DataStream 类型数据集,在 Flink 中 ConnectedStreams 提供的 map()方法和 flatMap()
9.Split 和 select [DataStream->SplitStream->DataStream]
Split 算子是将一个 DataStream 数据集按照条件进行拆分,形成两个数据集的过程, 也是 union 算子的逆向实现。每个接入的数据都会被路由到一个或者多个输出数据集中。如下图所示,将输入数据集根据颜色切分成两个数据集。
在使用 split 函数中,需要定义 split 函数中的切分逻辑,通过调用 split 函数,然后指定条件判断函数,如下面的代码所示:将根据第二个字段的奇偶性将数据集标记出来,如 果是偶数则标记为 even,如果是奇数则标记为 odd,然后通过集合将标记返回,最终生成格式 SplitStream 的数据集。
//创建数据集 val dataStream1: DataStream[(String, Int)] = env.fromElements(("a", 3), ("d", 4), ("c", 2), ("c", 5), ("a", 5)) //合并两个DataStream数据集 val splitedStream: SplitStream[(String, Int)] = dataStream1.split(t => if (t._2 % 2 == 0) Seq("even") else Seq("odd")) |
split 函数本身只是对输入数据集进行标记,并没有将数据集真正的实现切分,因此需要借助 Select 函数根据标记将数据切分成不同的数据集。如下代码所示, 通过调用SplitStream 数据集的 select()方法,传入前面已经标记好的标签信息,然后将符合条件的数据筛选出来,形成新的数据集。
/筛选出偶数数据集 val evenStream: DataStream[(String, Int)] = splitedStream.select("even") //筛选出奇数数据集 val oddStream: DataStream[(String, Int)] = splitedStream.select("odd") //筛选出奇数和偶数数据集 val allStream: DataStream[(String, Int)] = splitedStream.select("even", "odd") |
4.函数类和富函数类
所有算子几乎都可以自定义一个函数类、富函数类作为参数:
常见的函数类有:
mapFuntion
reduceFuntion
。。。
富函数的接口有:
- RichMapFunction
- RichFlatMapFunction
- RichFilterFunction 等
富函数接口和其他常规函数的接口不同在于:
可以获取运行环境的上下文,在上下文环境中可以管理状态(state)并拥有一些生命周期方法,所以可以实现更复杂的功能
Rich Function 有一个生命周期的概念。典型的生命周期方法有:
- open()方法是 rich function 的初始化方法,当一个算子例如 map 或者 filter 被调用之前 open()会被调用。
- close()方法是生命周期中的最后一个调用的方法,做一些清理工作。
- getRuntimeContext()方法提供了函数的 RuntimeContext 的一些信息,例如函数执行的并行度,任务的名字,以及 state 状态