一、flink基础核心知识
1、什么是Flink
2014年Flink作为主攻流计算的大数据引擎开始在开源大数据行业内崭露头角。区别于Storm、Spark Streaming 以及其他流式计算引擎的是:它不仅是一个高吞吐、低延迟的 计算引擎,同时还提供很多高级功能。比如它提供有状态的计算,支持状态管理,支持强一致性的数据语义以及支持Event Time,WaterMark 对消息乱序的处理等。
2015年是流计算百花齐放的时代,各个流计算框架层出不穷。Storm, JStorm, Heron,Flink, Spark Streaming,Google Dataflow(后来的Beam)等等。其中Flink的一致性语义和最接近Dataflow模型的开源实现,使其成为流计算框架中最耀眼的一颗。也许这也是阿里看中Flink的原因,并决心投入重金去研究基于Flink的Blink框架。
2、Flink的优点
1、支持批处理和数据流程序处理
2、优雅流畅的支持java和scala api
3、同时支持高吞吐量和低延迟
4、支持事件处理和无序处理通过SataStream API,基于DataFlow数据流模型
5、在不同的时间语义(事件时间,摄取时间、处理时间)下支持灵活的窗口(时间,滑动、翻滚,会话,自定义触发器)
6、仅处理一次的容错担保
7、自动反压机制
8、图处理(批) 机器学习(批) 复杂事件处理(流)
9、在dataSet(批处理)API中内置支持迭代程序(BSP)
10、高效的自定义内存管理,和健壮的切换能力在in-memory和out-of-core中
11、兼容hadoop的mapreduce和storm
12、集成YARN,HDFS,Hbase 和其它hadoop生态系统的组件
3、flink 与 sparkStreaming的区别
1、计算模型
flink:流计算
spark:微批处理
2、时间语义
flink:事件时间、处理时间
spark:处理时间
3、窗口
flink:多,灵活
spark:少,不灵活(窗口必须是批次的整数倍)
4、状态
flink:有
spark:没有
5、流式SQL
flink:有
spark:没有
二、flink系统架构
1、JobManager(主节点):集群大脑,负责任务调度、资源管理、Checkpoint 协调、客户端接入;
1、 内嵌 ResourceManager,管理整个集群的 Slot 资源;
2、可配置 HA(多 JobManager 主备)避免单点故障。
2、TaskManager(从节点):工作节点,独立 JVM 进程,负责实际执行计算任务;
1、每个 TaskManager 管理固定数量的 Slot(预配置);
2、启动后主动向 JobManager 注册,领取任务。
3、客户端:提交作业的节点,负责生成 JobGraph 并提交给 JobManager。
4、运行流程
- 集群启动:先启动 JobManager,再启动所有 TaskManager;TaskManager 主动向 JobManager 注册,汇报 Slot 资源;
- 作业提交 :客户端执行
flink run,生成 JobGraph 提交给 JobManager; - 调度执行:JobManager 根据作业并行度申请 Slot,将 Task 分发到对应 TaskManager;
- 任务执行:TaskManager 启动线程执行 Task,通过网络交互数据,定期汇报状态。
5、并行度
1、定义:
同一个逻辑算子被拆分成独立的子任务(SubTask)同时运行的个数为并行度,每个子任务处理一部分数据。一个流程序的并行度,是其所有算子中最大的并行度。一个程序中不同算子可能有不同的并行度。
2、并行度的 4 个设置层级(优先级从高到低)
① 算子级(最高优先级)
在代码中针对单个算子单独设置并行度,粒度最细,仅作用于当前算子。
java
// 给 map 算子设置并行度为 4
dataStream.map(x -> x * 2).setParallelism(4)
② 执行环境级
针对整个作业的所有算子设置默认并行度,算子级未设置时生效
java
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(6); // 整个作业所有算子默认并行度为 6
③ 客户端提交级
提交作业时通过命令行参数指定
powershell
./bin/flink run -m JobManager地址:8081 -p 8 -c 全类名 jar包路径
④ 集群配置级(最低优先级)
Flink 集群配置文件 flink-conf.yaml 中的全局默认值,所有作业未指定时生效。
powershell
parallelism.default: 1
6、算子链(Operator Chain)
1、定义
算子链是 Flink 的执行层优化机制 :在满足条件的前提下,把多个相邻的算子合并成一个算子链 ,作为一个独立的执行任务(Task),在同一个线程中运行。算子链的并行度 = 链内所有算子的并行度;
2、优点
① 减少线程切换开销:多个算子在同一个线程执行,避免线程间切换;
② 减少网络传输:同一算子链内的数据直接在内存中传递,不需要序列化 / 反序列化和网络通信;
③ 降低延迟、提升吞吐量:少了跨线程 / 跨节点的交互。
3、算子链的形成条件(同时满足以下 4 个条件)
- 并行度相同:上下游算子的并行度必须一致;
- Forward 分区器 :上下游之间是一对一的数据转发(
forward分区),没有 shuffle、没有数据重分区; - 未禁用算子链:代码中没有手动禁用算子链优化;
- 同一个 Slot 共享组 :上下游算子属于同一个 Slot 共享组(默认都属于
default组)。
4、自定义算子链
①.disableChaining():彻底禁用当前算子的算子链,前后都断开;env.是全局禁用,也可以单个算子禁用
②.startNewChain():从当前算子开始开启一条新的算子链,和上游断开。
7、任务槽(Task Slot)
1、定义
任务槽(Task Slot)是 TaskManager 中资源分配的最小单位 ,每个 Slot 对应 TaskManager 的一部分内存资源。
2、特点
① 内存隔离 :每个 Slot 拥有独立的内存空间,不同 Slot 之间的内存不共享,避免任务互相抢占内存;
② CPU 共享 :Slot 只隔离内存,不隔离 CPU,CPU 所有 Slot 共享;
3、Slot 的数量与配置
每个 TaskManager 的 Slot 数量是在配置文件 flink-conf.yaml 里修改参数taskmanager.numberOfTaskSlots:1 静态配置 的,启动后固定不变,生产环境通常设置为和 TaskManager 的 CPU 核心数一致
4、Slot 共享机制
① 什么是 Slot 共享
同一个作业的不同 Task(不同算子/算子链)可以在同一个 Slot执行,前提是它们必须属于同一个 Slot 共享组,默认所有算子都属于 default 共享组,自动开启共享;可以通过 .slotSharingGroup("group1") 手动指定分组,不同分组之间不能共享 Slot。也就是说:一个 Slot 里可以同时运行多个不同(算子/算子链)的 Task,比如 source 算子的 Task 和keyBy 算子的 Task 可以放在同一个 Slot 里同时运行。
② Slot 共享的优点
提高资源利用率,避免轻量算子独占资源造成浪费;负载均衡,Task 自动均匀分布到各个 Slot,避免部分节点过载。
三、flink部署模式
1、本地模式(Local Mode)
纯开发调试模式,所有 Flink 角色都运行在同一个 JVM 进程中,用多线程模拟分布式并行,不具备真实分布式能力,仅用于验证代码逻辑、单元测试。在 IDEA 里直接运行 Flink 代码,本质就是本地模式。
2、独立集群模式(Standalone Mode)
Flink 自带的独立分布式集群模式,不依赖任何外部资源调度框架,Flink 自己管理集群资源,是最基础的分布式部署形态。如果资源不足或出现故障,没有自动扩展或重分配资源的保证,必须手动处理。所以一般只用在测试或作业很少的场景
(1)会话模式
1、原理:
需要先启动一个集群,保持一个会话,在这个会话中通过客户端提交作业,集群启动时所有资源就都已经确定,所有提交的作业会竞争集群中的资源。比较适用于单个规模小,执行时间短的大量作业。
powershell
#启动集群命令
./bin/start-cluster.sh
#命令行提交作业
./bin/flink run -m JobManager地址:8081 -c 全类名 jar包路径
#停止集群命令
./bin/stop-cluster.sh
(2)应用模式
1、原理:
会话模式代码都是在客户端上执行,然后由客户端提交给JobManager的。应用模式直接把应用提交到JobManager上运行。也就代表我们需要为每一个提交的应用单独启动一个JobManager(集群)。
powershell
#应用程序的jar包必须放在lib/目录下
#执行以下命令启动JobManager
./bin/standalone-job.sh start --job-classname 全类名
#自己启动TaskManager,需要几个启动几个
./bin/taskmanager.sh start
#停止集群
./bin/taskmanager.sh stop
./bin/standalone-job.sh stop
3、YARN 部署模式
Flink 把资源调度完全交给 YARN,按需申请资源,和 Hadoop 生态深度打通,是目前大厂最主流的部署方式
(1)会话模式
1、基于yarn原理:
客户端把flink应用提交给yarn的ResourceManager,yarn的ResourceManager会向yarn的NodeManager申请容器,在容器上,flink会部署JobManager和TaskManager的实例,从而启动集群。flink会根据运行在JobManager上的作业所需要的slot数量动态分配TaskManager资源。所有作业都提交到这个共享集群里运行
powershell
#启动一个session集群,对yarn来讲就是启动了一个应用
./bin/yarn-session.sh -d -nm sessionTest
#命令行提交任务
./bin/flink run -d -c 全类名 jar包路径 #会自动提交到启动session集群因为/tmp/.yarn-properties文件会记录集群的ip端口直接连接
#命令行集群关闭
echo "stop" | ./bin/yarn-session.sh -id 应用id(application-开头的)
(2)单作业模式
会话模式因为资源共享会导致很多问题,所以为了更好的隔离资源,考虑为每个作业启动一个集群,就是单作业模式,作业完成后,集群会关闭,所有资源也会释放。但flink本身无法直接这样运行,一般需要借助一些资源管理框架来启动集群,比如yarn,k8s.
powershell
#命令行提交任务
./bin/flink run -d -t yarn-per-job -c 全类名 jar包路径
#停止作业
./bin/flink list -t yarn-per-job -Dyarn.application.id= application开头的id #获取对应的jobid
./bin/flink cancel -t yarn-per-job -Dyarn.application.id= application开头的id 刚获取的jobid
(3)应用模式
前面两种模式下应用代码都是在客户端上执行,然后由客户端提交给JobManager的,但是这样客户端需要占用大量网络带宽,去下载依赖和把二进制数据发送给JobManager,很多情况下我们提交作业用的是同一个客户端,就会加重客户端所在节点的资源消耗。所以我们不要客户端了,直接把应用提交到JobManager上运行。也就代表我们需要为每一个提交的应用单独启动一个JobManager(集群)。
powershell
#命令行提交任务
./bin/flink run-application -t yarn-application -c 全类名 jar包路径
#停止作业
./bin/flink list -t yarn-application -Dyarn.application.id= application开头的id #获取对应的jobid
./bin/flink cancel -t yarn-application -Dyarn.application.id= application开头的id 刚获取的jobid
四、批处理与流处理实例
1、批处理
java
public class WordCountBath{
public static void main(String[] args) throws Exception {
//创建执行环境
ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
//读取数据
DataSource<String> linedata = env.readTextFile("文件路径");
//切分,转换数据 (flink word ,转换成(flink,1)(word,1))
FlatMapOperator<String,Tuple2<String,Integer>> wordAndOne = linedata.flatMap(new FlatMapFunction<String,Tuple2<String,Integer>>(){
@Override
public void flatMap(String value,Collector<Tuple2<String,Integer>> out) throws Exception {
//按照空格切分单词
String[] words = value.split(" ");
//将单词转换为(flink,1)
for (String word : words){
Tuple2<String,Integer> wordTuple2 = Tuple2.of(word,1);
//使用Collector向下游发送数据
out.collect(wordTuple2);
}
}
});
//按照word分组
UnsortedGrouping<Tuple2<String,Integer>> wordAndOneGroupBy = wordAndOne.groupBy(0);
//分组内聚合
AggregateOperator<Tuple2<String,Integer>> sum = wordAndOneGroupBy.sum(1);//1是位置,表示第二个元素
//输出
sum.print();
}
}
2、流处理
java
public class WordCountStream{
public static void main(String[] args) throws Exception {
//创建执行环境
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
//读取数据
DataStreamSource<String> linedata = env.readTextFile("文件路径"); //有界流
DataStreamSource<String> socketdata = env.socketTextStream("hadoop102",8888); //无界流
//切分,转换数据 (flink word ,转换成(flink,1)(word,1))
SingleOutputStreamOperator<Tuple2<String,Integer>> wordAndOne = linedata.flatMap(new FlatMapFunction<String,Tuple2<String,Integer>>(){
@Override
public void flatMap(String value,Collector<Tuple2<String,Integer>> out) throws Exception {
//按照空格切分单词
String[] words = value.split(" ");
//将单词转换为(flink,1)
for (String word : words){
Tuple2<String,Integer> wordTuple2 = Tuple2.of(word,1);
//使用Collector向下游发送数据
out.collect(wordTuple2);
}
}
});
//按照word分组
KeyedStream<Tuple2<String,Integer>,String> wordAndOneKeyBy = wordAndOne.keyBy(new KeySelector<Tuple2<String,Integer>,String>(){
@Override
public String getKey(Tuple2<String,Integer> value) throws Exception{
return value.f0;
}
});
//分组内聚合
SingleOutputStreamOperator<Tuple2<String,Integer>> sum = wordAndOneKeyBy.sum(1);//1是位置,表示第二个元素
//输出
sum.print();
//执行:类似spark streaming最后的.start()
env.execute();
}
}