flink入门_Flink入门实战 (中)

14496727d090082566c3fedd621cadba.png

一、Flink 流处理 API

99b2bc78cd5abb0cb77c51173040d56a.png

1、Environment

  • getExecutionEnvironment
创建一个执行环境,表示当前执行程序的上下文。 如果程序是独立调用的,则
此方法返回本地执行环境;如果从命令行客户端调用程序以提交到集群,则此方法
返回此集群的执行环境,也就是说,getExecutionEnvironment 会根据查询运行的方
式决定返回什么样的运行环境,是最常用的一种创建执行环境的方式。

(1) 从集合中读取数据

具体代码实现:

package 

启动程序,控制台打印数据信息

4adf93f9684d7d5d4c4497547271438a.png

455c41fb8ac3de5af6e447eb16a37af8.png

(2)第二种方式:从文件读取数据

先在src/main/resources文件目录下创建sensor.txt文本,里面编辑的内容如下:

sensor_1

代码实现:

package 

启动程序,控制台打印数据信息

31ace1ffddf35dd93ab4eb8831afc1be.png

另外:

6f50ef23e469616395c9236cecccf491.png

309e7294a346fec597b839701f3e6208.png

(3)第三种方式:从kafka中读取数据

  • 需要在pom文件引入 kafka 连接器的依赖
<
  • 编写代码

基本代码实现:

val 

具体代码实现:

package 
  • 先把kakfka进程启动起来
[
  • 创建topic
//创建一个topic名叫sensor
[root@spark2 kafka]# bin/kafka-topics.sh --create --zookeeper spark2.x:2181 --partitions 2 --replication-factor 1 --topic sensor 
Created topic "sensor".

//创建生产者
[root@spark2 kafka]# bin/kafka-console-producer.sh --broker-list spark2.x:9092 --topic sensor

// 先把idea代码程序跑起来,之后我们就可以在kafka环境生产数据
>sensor_1,  1547718199,  35.80018327300259
>sensor_6,  1547718201,  15.402984393403084
>sensor_7,  1547718202,  6.720945201171228
>sensor_1,  1547718199,  35.80018327300259
>sensor_10, 1547718205,  38.101067604893444        

之后,先把idea代码程序跑起来,之后我们就可以在kafka环境生产数据了,如图所示:

be9adbf6f96a0ce3742a2ba09f84b5fb.png

总结:

往大方面想,比如:保证kafka数据的容错性、一致性,在集群运行环境过程中,若机器发生宕机了,如何解决?涉及检查点,而这个检查点(Checkpoints),它原理机制:就是存盘

当我们使用kafka把数据读进来后,它 的偏移量就发生改变,若想把kakfa的数据进行再消费一遍,如何解决?

在spark中

第一种方式:,在整个处理完成之后,加一个机制,不要让它消费,直接把它的偏移量更改,到最后整个完成之后,再把它的偏移量做更改

第二种方式:在恢复之前后的状态的保存点的数据都要存下来,恢复的时候在之前kafka的偏移量手动更改,重新提交一次,相当于告诉kafka,重新再消费一次

在Flink中

与spark有所不同,区别:spark是一批一批的读取数据,而flink是一条一条的读取数据;

另外,flink的特性是:有状态的流处理,在这种过程中可以保证状态,所以可以把我们kafka的偏移量作为状态保存下来,言下之意:等到恢复的时候有检查点机制,后面处理的过程中也挂了,在之前存盘的重新读取、重新恢复,就相当于把之前偏移量恢复出来,然后flink本身的kafka连接器实现了,相当于重新手动提交的偏移量

(4)第四种方式:自定义数据源

我们希望可以随机生成传感器数据,基本代码实现:

class 

具体代码实现:

package 

启动程序,控制台打印数据信息:数据不断产生、时间戳、温度不断变化

3d903524daad8a918b9e6bfb753a37f8.png
数据不断产生https://www.zhihu.com/video/1241021669457362944

2、Transform--转换算子

(1)map

58e0db1ca09333354028d3c3b05b7125.png
val 

(2)flatMap

flatMap 的函数签名:def flatMap[A,B](as: List[A])(f: A ⇒ List[B]): List[B]
例如: flatMap(List(1,2,3))(i ⇒ List(i,i))
结果是 List(1,1,2,2,3,3),
而 List("a b", "c d").flatMap(line ⇒ line.split(" "))
结果是 List(a, b, c, d)。
val 

(3) Filter

f982b7535a95c1ebc6541a718488abe6.png
val 

(4)KeyBy

1d163fc90ad482c4a8fd3b0a0f611e1b.png

DataStream → KeyedStream:逻辑地将一个流拆分成不相交的分区,每个分

区包含具有相同 key 的元素,在内部以 hash 的形式实现的。

(5) 滚动聚合算子(Rolling Aggregation)

这些算子可以针对 KeyedStream 的每一个支流做聚合。

  •  sum()
  •  min()
  •  max()
  •  minBy()
  •  maxBy()

(6)Reduce

KeyedStream → DataStream:一个分组数据流的聚合操作,合并当前的元素
和上次聚合的结果,产生一个新的值,返回的流中包含每一次聚合的结果,而不是
只返回最后一次聚合的最终结果。
val 

具体代码实现:基本转换算子

package 

启动程序,控制台打印数据信息:

ec714bdc039d7def5ef1a22a087d5821.png

需求:输出当前传感器最新的温度+10,而时间戳是上一次数据 时间+1

具体代码实现:聚合算子

package 

启动程序,控制台打印数据信息:

d4769770af78e2f2613d9399a2b32ed9.png

(7)Split 和 Select(spile分流操作

a7b6dbd67469ab350a04e0ec2a6bfe60.png
DataStream → SplitStream:根据某些特征把一个 DataStream 拆分成两个或者
多个 DataStream。

ddb2d7869e7f519b959606e61bff72f9.png
SplitStream→DataStream:从一个 SplitStream 中获取一个或者多个 DataStream。
需求:传感器数据按照温度高低(以 30 度为界),拆分成两个流。

spile分流,具体代码实现

package 

启动程序,控制台打印数据信息:

14a667fba9eeefdb9f4d6a47b58b9d3d.png

(8)Connect 和 CoMap (合并操作)

8d44060c352d72529b0df303776a1584.png
DataStream,DataStream → ConnectedStreams:连接两个保持他们类型的数据
流,两个数据流被 Connect 之后,只是被放在了一个同一个流中,内部依然保持各
自的数据和形式不发生任何变化,两个流相互独立。

fbb10e6a6cdb069d04c52b65cc02e4c6.png
ConnectedStreams → DataStream:作用于 ConnectedStreams 上,功能与 map
和 flatMap 一样,对 ConnectedStreams 中的每一个 Stream 分别进行 map 和 flatMap
处理。

具体代码实现:

package 

启动程序,控制台打印数据信息:

82d4c92415f9ae69e642c30561af82ea.png

3158ece496ee63307b037f3316fc145a.png
DataStream → DataStream:对两个或者两个以上的 DataStream 进行 union 操
作,产生一个包含所有 DataStream 元素的新 DataStream。
//合并以后打印

Connect 与 Union 区别:

  • Union 之前两个流的类型必须是一样,Connect 可以不一样,在之后的 coMap 中再去调整成为一样的。
  • Connect 只能操作两个流,Union 可以操作多个

union方式,具体代码实现:

package 

启动程序,控制台打印数据信息:

fc54d56d2f973290a4d6cc83f0d76dfd.png

3、支持的数据类型

Flink 流应用程序处理的是以数据对象表示的事件流。所以在 Flink 内部,我们
需要能够处理这些对象。它们需要被序列化和反序列化,以便通过网络传送它们;
或者从状态后端、检查点和保存点读取它们。为了有效地做到这一点,Flink 需要明
确知道应用程序所处理的数据类型。Flink 使用类型信息的概念来表示数据类型,并
为每个数据类型生成特定的序列化器、反序列化器和比较器。
Flink 还具有一个类型提取系统,该系统分析函数的输入和返回类型,以自动获
取类型信息,从而获得序列化器和反序列化器。但是,在某些情况下,例如 lambda
函数或泛型类型,需要显式地提供类型信息,才能使应用程序正常工作或提高其性
能。

Flink 支持 Java 和 Scala 中所有常见数据类型。使用最广泛的类型有以下几种。

  • 基础数据类型

Flink 支持所有的 Java 和 Scala 基础数据类型,Int, Double, Long, String, …

val 
  • Java 和 Scala 元组(Tuples)
val 
  • Scala 样例类(case classes)
case 
  • Java 简单对象(POJOs)
public 
  • 其它(Arrays, Lists, Maps, Enums, 等等)
Flink 对 Java 和 Scala 中的一些特殊目的的类型也都是支持的,比如 Java 的
ArrayList,HashMap,Enum 等等。

4、实现 UDF 函数——更细粒度的控制流

(1)函数类(Function Classes)

Flink 暴露了所有 udf 函数的接口(实现方式为接口或者抽象类)。例如
MapFunction, FilterFunction, ProcessFunction 等等。
下面例子实现了 FilterFunction 接口:
class 

还可以将函数实现成匿名类

val 

我们 filter 的字符串"flink"还可以当作参数传进去

val 

具体代码实现:

package 

启动程序,控制台打印数据信息:

a0afe6ca28ea22adc672c08321a524d7.png

(2)匿名函数(Lambda Functions)

dataStream

(3)富函数(Rich Functions)

“富函数”是 DataStream API 提供的一个函数类的接口,所有 Flink 函数类都
有其 Rich 版本。它与常规函数的不同在于,可以获取运行环境的上下文,并拥有一
些生命周期方法,所以可以实现更复杂的功能。
  •  RichMapFunction
  •  RichFlatMapFunction
  •  RichFilterFunction
  •  …

Rich Function 有一个生命周期的概念。典型的生命周期方法有:

  •  open()方法是 rich function 的初始化方法,当一个算子例如 map 或者 filter

被调用之前 open()会被调用。

  •  close()方法是生命周期中的最后一个调用的方法,做一些清理工作。
  •  getRuntimeContext()方法提供了函数的 RuntimeContext 的一些信息,例如函

数执行的并行度,任务的名字,以及 state 状态

class 

5、Sink

Flink 没有类似于 spark 中 foreach 方法,让用户进行迭代的操作。虽有对外的
输出操作都要利用 Sink 完成。最后通过类似如下方式完成整个任务最终输出操作。
stream

官方提供了一部分的框架的 sink。除此以外,需要用户自定义实现 sink。

c8b581a8e60e34a0cc70c209fe945d97.png

41b5118bf4d5ae62ea712b12904c1c3d.png

(1)pom.xml 引入

<

(2)Kafka Sink代码编写

package 

(3)虚拟机创建kafka的消费者

[

(4 ) 启动程序,控制台打印数据信息:

d84bfd6c764210f6c4f3cf1da50f301c.png

上面这种方式方便测试,而在工作当中,我们在获取数据不是在文件读数据的,我们使用kafka一边生产数据一边消费数据,这种方式需要如下操作:

  • Kafka Sink代码编写
package 
  • 虚拟机在kafka创建生产数据与消费数据
//启动kafka服务
  • 启动idea代码程序,当我们在虚拟机的kafka生产数据,控制台也将会打印出来,同时消费者也会消费生产者的 数据,如图所示:

3b5fcf51edcb65066d6ac80400b5ed06.png

视频演示:

64f82473cb2855ce0495e85a9dc25cb9.png
Kafka Sink生产与消费https://www.zhihu.com/video/1241138716938530816

6、Redis 的Sink操作

(1)pom.xml 引入

<

(2)Redis 代码编写

package 

(3)虚拟机命令行基本操作

//启动redis服务

(4)启动idea代码的程序,控制台打印无数据,表示跑完了

4b282cbdef589bd753e1a24152f50c05.png

(5)再次到redis的数据库查看

查看当前的数据,发现数据进来了

7、Elasticsearch的Sink操作

(1)pom.xml 引入

<

(2)Elasticsearch代码编写

package 

(3) 配置Elasticsearch环境,参照自己写的博客:大数据ELK环境平台搭建

查看当前所有的索引:http://spark2.x:9200/_cat/indices?v

a3079678d6b018e0bb23b39cfca068fe.png

(4)启动idea代码的程序,控制台打印出数据

f8edce07bc235d8cf5ff2abe46c3946c.png

a99df076cd84b47fb33b27f1d118cf06.png

当然,若想查看数据的详细信息,可以这样查看:http://spark2.x:9200/sensor

34cc7e035519ef7be109f97c5867d3c6.png

8、JDBC的Sink操作

(1)pom.xml 引入

<

(2)代码编写

package 

(3)虚拟机mysql数据库基本创建

//创建test数据库

(4)启动idea代码的程序,控制台打印无数据,表示跑完了

94616a72ca97353fa7ae989b9d507b9d.png

( 5) 在虚拟机查看数据,进来了

mysql

六、Flink 中的 Window

1、Window

1.1、window 概述

streaming 流式计算是一种被设计用于处理无限数据集的数据处理引擎,而无限
数据集是指一种不断增长的本质上无限的数据集,而 window 是一种 切割无限数据 为有限块进行处理的手段。
Window 是无限数据流处理的核心,Window 将一个无限的 stream 拆分成有限大
小的”buckets”桶,我们可以在这些桶上做计算操作。

1.2、window 类型

Window 可以分成两类:

  •  CountWindow:按照指定的数据条数生成一个 Window,与时间无关。
  •  TimeWindow:按照时间生成 Window。

对于 TimeWindow,可以根据窗口实现原理的不同分成三类:滚动窗口(Tumbling

Window)、滑动窗口(Sliding Window)和会话窗口(Session Window)。

  1. 滚动窗口(Tumbling Windows)
将数据依据固定的窗口长度对数据进行切片。 特点:时间对齐,窗口长度固定,没有重叠。
滚动窗口分配器将每个元素分配到一个指定窗口大小的窗口中,滚动窗口有一
个固定的大小,并且不会出现重叠。

例如:如果你指定了一个 5 分钟大小的滚动窗 口,窗口的创建如下图所示:

7098a24ea49146da9fe548eccc14853f.png

适用场景:适合做 BI 统计等(做每个时间段的聚合计算)。

2. 滑动窗口(Sliding Windows)

滑动窗口是固定窗口的更广义的一种形式,滑动窗口由固定的窗口长度和滑动
间隔组成。 特点:时间对齐,窗口长度固定,可以有重叠。
滑动窗口分配器将元素分配到固定长度的窗口中,与滚动窗口类似,窗口的大
小由窗口大小参数来配置,另一个窗口滑动参数控制滑动窗口开始的频率。因此,
滑动窗口如果滑动参数小于窗口大小的话,窗口是可以重叠的,在这种情况下元素
会被分配到多个窗口中。

例如,你有 10 分钟的窗口和 5 分钟的滑动,那么每个窗口中 5 分钟的窗口里包

含着上个 10 分钟产生的数据,如下图所示:

5ec77acbb1e271c034652073c56f940b.png

适用场景:对最近一个时间段内的统计(求某接口最近 5min 的失败率来决定是

否要报警)。

3. 会话窗口(Session Windows)

由一系列事件组合一个指定时间长度的 timeout 间隙组成,类似于 web 应用的

session,也就是一段时间没有接收到新数据就会生成新的窗口。

特点:时间无对齐。
session 窗口分配器通过 session 活动来对元素进行分组,session 窗口跟滚动窗
口和滑动窗口相比,不会有重叠和固定的开始时间和结束时间的情况,相反,当它
在一个固定的时间周期内不再收到元素,即非活动间隔产生,那个这个窗口就会关
闭。一个 session 窗口通过一个 session 间隔来配置,这个 session 间隔定义了非活跃
周期的长度,当这个非活跃周期产生,那么当前的 session 将关闭并且后续的元素将
被分配到新的 session 窗口中去。

b87b07969fd6b3d382c0c0b2047af8b5.png

2、 Window API

2.1、TimeWindow

TimeWindow 是将指定时间范围内的所有数据组成一个 window,一次对一个

window 里面的所有数据进行计算。

(1)滚动窗口

Flink 默认的时间窗口根据 Processing Time 进行窗口的划分,将 Flink 获取到的

数据根据进入 Flink 的时间划分到不同的窗口中。

val 

时间间隔可以通过 Time.milliseconds(x),Time.seconds(x),Time.minutes(x)等其

中的一个来指定。

(2)滑动窗口(SlidingEventTimeWindows)

滑动窗口和滚动窗口的函数名是完全一致的,只是在传参数时需要传入两个参
数,一个是 window_size,一个是 sliding_size。

下面代码中的 sliding_size 设置为了 5s,也就是说,窗口每 5s 就计算一次,每

一次计算的 window 范围是 15s 内的所有元素。

val 

时间间隔可以通过 Time.milliseconds(x),Time.seconds(x),Time.minutes(x)等其

中的一个来指定。

2.2、CountWindow

CountWindow 根据窗口中相同 key 元素的数量来触发执行,执行时只计算元素
数量达到窗口大小的 key 对应的结果。
注意:CountWindow 的 window_size 指的是相同 Key 的元素的个数,不是输入
的所有元素的总数。
  • 滚动窗口
默认的 CountWindow 是一个滚动窗口,只需要指定窗口大小即可,当元素数量
达到窗口大小时,就会触发窗口的执行。
val 
  • 滑动窗口
滑动窗口和滚动窗口的函数名是完全一致的,只是在传参数时需要传入两个参
数,一个是 window_size,一个是 sliding_size。
下面代码中的 sliding_size 设置为了 2,也就是说,每收到两个相同 key 的数据
就计算一次,每一次计算的 window 范围是 5 个元素。
val 

2.3、 window function

window function 定义了要对窗口中收集的数据做的计算操作,主要可以分为两

类:

  • 增量聚合函数(incremental aggregation functions)

每条数据到来就进行计算,保持一个简单的状态。典型的增量聚合函数有 ReduceFunction, AggregateFunction。

  • 全窗口函数(full window functions)

先把窗口所有数据收集起来,等到计算的时候会遍历所有数据。

ProcessWindowFunction 就是一个全窗口函数。

2.4、其它可选 API

  • .trigger() —— 触发器

定义 window 什么时候关闭,触发计算并输出结果

  • .evitor() —— 移除器

定义移除某些数据的逻辑

  • .allowedLateness() —— 允许处理迟到的数据
  •  .sideOutputLateData() —— 将迟到的数据放入侧输出流
  •  .getSideOutput() —— 获取侧输出流

0de93606006f5e5ac563e287d4f5c94b.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值