Apache Flink
概述
Flink是构建在数据流之上的有状态计算的流计算框架,通常被人们理理解为是 第三代 大数据分析方案。
第一代 - Hadoop的MapReduce计算(静态)、Storm流计算(2014.9) ;两套独立计算引擎,使用难度大
第二代 - Spark RDD 静态批处理理(2014.2)、DStream|Structured Streaming
流计算;统一计算引擎,难度系数小
第三代 - Flink DataStream(2014 .12) 流计算框架、Flink Dataset
批处理理;统一计算引擎,难度系数不低也不高可以看出Spark和Flink几乎同时诞生,但是Flink之所以发展慢,是因为早期人们对大数据的分析的认知不够深刻或者当时业务场景大都局限在批处理领域,从而导致了Flink的发展相比较于Spark较为缓慢 ,直到2016年人们才开始慢慢的意识流计算的重要性。
流计算领域:系统监控、舆情监控、交通预测、国家电网、疾病预测、银行/金融风控等。
概念
Task和Operator Chain
Flink是一个分布式流计算引擎,该引擎将一个计算job拆分成若干个Task(等价于Spark中的Stage),每个Task都有自己的并行度,每个并⾏行行度都由一个线程表示,因为一个Task是并行执行的,因此一个Task底层对应一系列的线程,Flink称为这些线程为该Task的subtask。与Spark不同的地方在于Spark是通过RDD的依赖关系实现Stage的划分⽽而Flink是通过 OperatorChain 的概念实现Task的拆分。所谓的OperatorChain 指的是Flink在做job编织的时候,尝试将多个操作符算子进行串联到一个Task中,以减少数据的线程到线程传输的开销,目前Flink常见的Operatorchain的方式有两种:forward、hash|rebalance.
- Task - 等价spark中的Stage,每个Task都有若干个SubTask SubTask -
- 等价一个线程,是Task中的一个子任务 OperatorChain -
- 将多个算子归并到一个Task的一种机制,归并原则类似SparkRDD的宽窄依赖
JobManagers、TaskManagers、Clients
- JobManagers -
(也称为master)负责协调分布式执行。负责任务调度,协调检查点,协调故障恢复等,等价于Spark中的Master+Driver的功能。通常一个集群中至少有1个Active的JobManager,如果在HA模式下其他处于StandBy状态。 - TaskManagers - (称为Worker)
真正负责Task执行计算节点,同时需要向JobManager汇报自身状态信息和工作负荷。通常一个集群中有若干个TaskManager。 - client -与Spark不同,Flink中的Client并不是集群计算的一部分,Client仅负责提交任务的Dataflow
Graph给JobManager,提交完成之后,可以直接退出。因此该Client并不负责任务执行过程中调度。
Task Slots和Resources
每一个Worker(TaskManager)是一个JVM进程,可以一个或者多个子任务(Thread/SubTask)为了控Worker节点能够接受多个Task任务,Worker提出所谓Task slot用于表达一个计算节点的计算能力(每个计算节点至少有一个Task slot)。
每个TaskSlot表示的是TaskManager计算资源的固定子集。例如:如果一个TaskManager拥有3个TaskSlot,则每个Task Slot表示占用当前TaskManager的进程的1/3内存资源。每个Job(计算)启动的时候都拥有自己的固定的Task Slot,也就意味着避免了不同job间的在运行时产生内存资源抢占。这些被分配的Task Slot资源只能被当前job的所有Task所使用,不同Job的Task之间不存在资源共享和抢占问题。但是一个Job会被拆分成若干个Task,每个 Task由若干个SubTask构成(取决于Task并行度)。默认Task Slot所对应的内存资源只能在同一个Job下的不同的Task进行共享,也就意味着同一个Task的不同subtask不能运行在同一个Taskslot中,**但是如果是相同job的不同Task的SubTask却可以。**如果同一个Job的不同Task的subtask不共用slot,会导致资源浪费。例如下图source、map 操作定位
为资源稀疏性操作,因为该操作占用内存量小,而 keyBy/windows()/apply() 涉及Shu!le会占用大量的内存资源,定位为资源密集型操作,比较吃内存。
因此Flink底层默认做的是不同Task的子任务共享Task Slot资源。因此用户可以将 `source/map 和keyBy/windows()/apply() 所对应的任务的并行度进行调整,将并行度由上图中2调整6,这样Flink底层就会做如下资源分配:
同一个job的不同task是可以共槽的 , 同一个job的同一个task的不同subtask是不可以共槽的
(Source[1] map(1),Source[2] map(2),Source[3] map(2),Source[4],map(4)…是同一个job同一个task不同的四个subtask集 不能再同一个taskslot中 即横向)
(Source[1] map(1),keyby()/window() ,/sink()是同一个job不同的task 可以在一个tasksolt中 即纵向)
因此可以看出Flink默认行为是尝试将同一个job的下的不同Task的SubTask进行Task slot共享。也就意味着一个Job的运行所需要的Task Slot的个数应该等于该Job中Task并行度的最大值。当然用户也可以通过 程序干预Flink Task间Task Slot共享策略。
结论:Flink的job运行所需要的资源数是自动计算出来的,无需用户指定,用户只需指定计算并行度即可。
State Backends
Flink是一个基于状态计算流计算引擎,存储的key/value状态索引的确切数据结构取决于所选的StateBackend。例如:使用Memory State Backend将数据存储在内存中的HashMap中,或者使用RocksDB(内嵌NoSQL数据,和Derby数据库类似)作为State Backend 存储状态。除了定义保存状态的数据结构之外,State Backend还实现逻辑以获key/value状态的时间点快照并将该快照存储为Checkpoint的一部分。
Savepoints
用Data Stream API编写的程序可以从Savepoint恢复执行。Savepoint允许更新程序和Flink群集,而不会丢失任何状态。
Savepoint是手动触发的Checkpoint,Savepoint为程序创建快照并将其写到State Backend。Savepoint依靠常规的Checkpoint机制。所谓的Checkpoint指的是程序在执行期间,程序会定期在工作节点上快照并产Checkpoint。为了进行恢复,仅需要获取最后一次完成的Checkpoint即可,并且可以在新的Checkpoint完成后立即安全地丢弃较旧的Checkpoint。
Savepoint与这些定期Checkpoint类似,Savepoint由用户触发并且更新的Checkpoint完成时不会自动过期。用户可以使用命令行或通过REST API取消作业时创建Savepoint
环境安装
前提条件
- JDK必须是1.8+,完成JAVA_HOME配置
- 安装Hadoop、并保证HADOOP正常运⾏行行(SSH免密码、HADOOP_HOME)
Flink 安装(Standalone)
上传并解压
[root@CentOS ~]# tar -zxf flink-1.10.0-bin-scala_2.11.tgz -C /usr/
[root@CentOS flink-1.10.0]# tree -L 1 ./
./
"## bin #执⾏行行脚本⽬目录
"## conf #配置⽬目录
"## examples #案例例jar
"## lib # 依赖的jars
"## LICENSE
"## licenses
"## log # 运⾏行行⽇日志
"## NOTICE
"## opt # 第三⽅方备⽤用插件包
"## plugins
$## README.txt
8 directories, 3 files
配置flink-conf.yaml
[root@CentOS flink-1.10.0]# vi conf/flink-conf.yaml
#==============================================================================
# Common
#==============================================================================
jobmanager.rpc.address: CentOS
taskmanager.numberOfTaskSlots: 4
parallelism.default: 3
配置salves
[root@CentOS flink-1.10.0]# vi conf/slaves
Centos
启动Flink
[root@CentOS flink-1.10.0]# ./bin/start-cluster.sh
Starting cluster.
Starting standalonesession daemon on host CentOS.
Starting taskexecutor daemon on host CentOS.
[root@CentOS flink-1.10.0]# jps
6978 NameNode
7123 DataNode
109157 StandaloneSessionClusterEntrypoint
7301 SecondaryNameNode
109495 TaskManagerRunner
109544 Jps
快速入门
wordcount案例
导入依赖
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-streaming-scala_2.11</artifactId>
<version>1.10.0</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-clients_2.11</artifactId>
<version>1.10.0</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-streaming-scala_2.11</artifactId>
<version>1.10.0</version>
<scope>provided</scope>
</dependency>
引入插件打包
<build>
<plugins>
<!--scala编译插件-->
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>4.0.1</version>
<executions>
<execution>
<id>scala-compile-first</id>
<phase>process-resources</phase>
<goals>
<goal>add-source</goal>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
<!--创建fatjar插件-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
<!--编译插件-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
wordCount程序
package com.baizhi.jsy.wordCount
import org.apache.flink.streaming.api.scala._
object FlinkWordCount {
def main(args: Array[String]): Unit = {
//1.创建流计算执⾏行行环境
val env = StreamExecutionEnvironment.getExecutionEnvironment
//2.创建DataStream - 细化
val text = env.socketTextStream("Centos",9999)
//3.执⾏行行DataStream的转换算⼦子
val counts = text.flatMap(line=>line.split("\\s+"))
.map(word=>(word,1))
.keyBy(0)
.sum(1)
//4.将计算的结果在控制打印
counts.print()
//5.执⾏行行流计算任务
env.execute("Window Stream WordCount")
}
}