DataSet:对静态数据进行批处理操作、将静态数据抽象为分布式数据集,使用Flink各种操符处理数据集,支持Java、Scala、Python
DataStream:对数据流进行流处理操作,将流式的数据抽象为分布式数据流,用Flink各种操作符处理数据流,支持Java、Scala
Table API:对结构化数据进行查询操作,将结构化数据抽象为关系表,并通过类SQL的DSL对关系表进行各种查询操作,支持Java、Scala
数据集:
数据处理模型:
Flink:将有界数据集当做无界数据集的一种特例
Spark Streaming:把无界数据集切割成有界,通过微批的方式进行流计算
Flink的三个基本组件:Source、Transformation(核心处理逻辑)和Sink
用户实现的Flink程序是由Stream和Transformation这两个基本构建块组成,其中Stream是一个中间结果数据,而Transformation是一个操作,它对一个或多个输入Stream进行计算处理,输出一个或多个结果Stream。当一个Flink程序被执行的时候,它会被映射为Streaming Dataflow。一个Streaming Dataflow是由一组Stream和Transformation Operator组成,它类似于一个DAG图,在启动的时候从一个或多个Source Operator开始,结束于一个或多个Sink Operator。 下面是一个由Flink程序映射为Streaming Dataflow的示意图,如下所示:
在Flink中,程序是并行和分布式的。一个Stream可以被分成多个Stream分区(Stream Partitions),一个Operator可以被分成多个Operator Subtask,每一个Operator Subtask是在不同的线程中独立执行的。一个Operator的并行度,等于Operator Subtask的个数,一个Stream的并行度总是等于生成它的Operator的并行度。
两个operator之间stream的两种模式:
比如从Source[1]到map()[1],它保持了Source的分区特性(Partitioning)和分区内元素处理的有序性,也就是说map()[1]的Subtask看到数据流中记录的顺序,与Source[1]中看到的记录顺序是一致的。
这种模式改变了输入数据流的分区,比如从map()[1]、map()[2]到keyBy()/window()/apply()[1]、keyBy()/window()/apply()[2],上游的Subtask向下游的多个不同的Subtask发送数据,改变了数据流的分区,这与实际应用所选择的Operator有关系。
在Flink分布式执行环境中,Operator Chain将多个Operator Subtask串起来组成tasks,实际上就是一个执行链,它减少了线程到线程切换和缓存的开销,降低延迟的同时提高了整体吞吐量。每个执行链会在TaskManager上一个独立的线程中执行。
Flink借鉴了Google的MillWheel项目,通过WaterMark来支持基于Event Time的时间窗口。
Flink使用WaterMark衡量事件时间,WaterMark携带时间戳t,并被插入到stream中。
Flink容错的核心:barrier(组标记栏)
以河水举例:
storm是一滴一滴处理数据
spark streaming像水坝,一批一批放水,上一批的水处完才放下一批水
Flink在水中定期插入barrier,水不停地流,只是加了些barrier。
注意:如果源头是多个数据流,那么都同步的增加同样的barrier,同时在job处理过程中,为了保证job失败时可以从错误中恢复,Flink对barrier进行对齐(align)操作
当Operator接收到多个输入的数据流时,需要在Snapshot Barrier中对数据流进行排列对齐:
基于Stream Aligning操作能够实现Exactly Once语义,但是也会给流处理应用带来延迟,因为为了排列对齐Barrier,会暂时缓存一部分Stream的记录到Buffer中,尤其是在数据流并行度很高的场景下可能更加明显,通常以最迟对齐Barrier的一个Stream为处理Buffer中缓存记录的时刻点。在Flink中,提供了一个开关,选择是否使用Stream Aligning,如果关掉则Exactly Once会变成At least once。
Flink的容错核心是保持分布式数据流和operator状态的snapshot一致性。Checkpoint并不仅仅是对数据流做了一个状态的snapshot,它也包含了一个Operator内部所持有的状态,这样才能够在保证在流处理系统失败时能够正确地恢复数据流处理。状态包含两种:
当operator在接收到所有输入流barrier n的快照和提交barrier n到输出流之前,
对operator的状态做快照。在下一个barrier出现之前,operator的状态快照更新到state,一旦这个barrier被应用之后,不能对其做任何更新操作。该operator的状态被存储到state之后,operator向checkpoint发出确认信号,然后提交快照barrier到输出流。
快照结果包含两种:
在JobManager端,会接收到Client提交的JobGraph形式的Flink Job,JobManager会将一个JobGraph转换映射为一个ExecutionGraph,ExecutionGraph是JobGraph的并行表示,也就是实际JobManager调度一个Job在TaskManager上运行的逻辑视图。
物理上进行调度,基于资源的分配与使用的一个例子:
概念:通常是由于某段时间内源头数据量的暴涨,导致流任务处理数据的速度远远小于源头数据的流入速度
导致问题:这种情况会导致流任务的内存越积越大,可能导致资源耗尽甚至系统崩溃
不同流计算引擎的处理方式不同:
默认情况下,JobManager会每间隔50ms触发对一个Job的每个Task依次进行100次堆栈跟踪调用,过计算得到一个比值,例如,radio=0.01,表示100次中仅有1次方法调用阻塞。Flink目前定义了如下Backpressure状态:
OK: 0 <= Ratio <= 0.10
LOW: 0.10 < Ratio <= 0.5
HIGH: 0.5 < Ratio <= 1
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nexxmjr2-1619947545387)(https://ci.apache.org/projects/flink/flink-docs-release-1.7/fig/processes.svg)]
默认情况下,只要运行同一个Flink job,Flink允许subtasks共享slots即使它们是不同tasks的subtasks。这样的结果是一个solt能保存一个job的完整pipeline。slot共享由以下两个好处:
key/values形式的数据结构被存储的位置取决于所选择的状态后端
除了定义保存state的数据结构,state backends实现了基于时间点对key/values做快照机制,并将快照作为checkpoint的一部分来存储。
Flink在YARN集群上运行时:Flink job所需要的jar包和配置文件等先上传到HDFS中,Flink YARN Client负责与YARN RM通信协商资源请求,Flink JobManager和Flink TaskManager分别申请到Container去运行各自的进程。
YARN AM与Flink JobManager在同一个Container中,这样AM可以知道Flink JobManager的地址,从而AM可以申请Container去启动Flink TaskManager。待Flink成功运行在YARN集群上,Flink YARN Client就可以提交Flink Job到Flink JobManager,并进行后续的映射、调度和计算处理。
hostname | IP |
---|---|
master | 192.168.63.20 |
slave1 | 192.168.63.21 |
slave2 | 192.168.63.22 |
[root@master src]# wget https://archive.apache.org/dist/flink/flink-1.4.0/flink-1.4.0-bin-hadoop26-scala_2.11.tgz
[root@master flink-1.4.0]# tar -zxvf /usr/local/src/flink-1.4.0-bin-hadoop26-scala_2.11.tgz -C /usr/local/
(1)修改conf/flink-conf.yaml
将jobmanager.rpc.address修改为集群jobmanager的hostname/IP
(2)修改masters文件
master:8081
(3)修改slaves文件
slave1
slave2
(4) 将flink目录拷贝到slave1和slave2中
[root@master flink-1.4.0]# scp -r /usr/local/flink-1.4.0 root@slave1:/usr/local/
[root@master flink-1.4.0]# scp -r /usr/local/flink-1.4.0 root@slave2:/usr/local/
master
[root@master flink-1.4.0]# ./bin/start-cluster.sh
添加一个jobmanager
[root@master flink-1.4.0]# ./bin/jobmanager.sh start cluster|stop|stop-all
添加一个taskmanager
[root@master flink-1.4.0]# ./bin/taskmanager.sh start|stop|stop-all
http://master:8081
master
slave
//导入flink scala 包
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.windowing.time.Time
object FlinkTest {
def main(args: Array[String]): Unit = {
// batch操作
// val benv = ExecutionEnvironment.getExecutionEnvironment
// val path = "/home/cqupt/Documents/data"
// val dataSet = benv.readTextFile(s"$path/test.txt")
// dataSet.map(_.split(",")(0)).map((_,1L)).groupBy(0).sum(1).print()
// stream操作
// 构造一个stream的env
val senv = StreamExecutionEnvironment.getExecutionEnvironment
val dataStream = senv.socketTextStream("master",9999)
// 对数据流进行逻辑处理
val wordcount = dataStream.map(x=>WordCount(x,1))
.keyBy("word")
// 输出5s时间窗口里所有元素的第一个元素,并将所有元素数量统计出来
.timeWindowAll(Time.seconds(5),Time.seconds(1))
// .max("count")
// 事件窗口里所有元素数量上限为5,元素输出步长为2,输出为窗口第一个到达的元素
// .countWindowAll(5,2)
// 时间窗口:窗口长度为5s,窗口每隔1s滑动一次
// .timeWindow(Time.seconds(5),Time.seconds(1))
// 事件窗口:窗口某一个元素数量上限为10,每一个元素出现2次才进行输出,某个元素数量达到10后,再输入这个元素输出依旧为10
// .countWindow(10,2)
.sum("count")
wordcount.print()
senv.execute("stream")
}
// 定义一个class
case class WordCount(word:String,count:Long)
}
输出结果
[root@master flink-1.4.0]# cd /usr/local/flink-1.4.0/
[root@master flink-1.4.0]# ./bin/start-cluster.sh
slave2提前开启监听端口号9999
[root@slave2 ~]# nc -lp 9999
将jar包提交到集群
[root@master flink-1.4.0]# ./bin/flink run examples/streaming/SocketWindowWordCount.jar --port 9999
查看监控页面 master:8081
输出结果如下
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/usr/local/flink-1.4.0/lib/slf4j-log4j12-1.7.7.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/usr/local/hadoop-2.6.1/share/hadoop/common/lib/slf4j-log4j12-1.7.5.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]
fa : 3
fsa : 1
af : 1
a : 6
as : 1
f : 7
: 3
关闭监听端口9999即可
[root@master flink-1.4.0]# ./bin/yarn-session.sh -n 2 -s 1 -jm 1024 -tm 1024
[root@slave1 ~]# nc -lp 9999
[root@master flink-1.4.0]# ./bin/flink run examples/streaming/SocketWindowWordCount.jar --port 9999
查看输出结果