Flink深入浅出: 应用部署与原理图解(v1.11)
Flink在1.11版本新增了一种部署模式,目前支持三种:Session 模式、Per job 模式、Application 模式,这三种模式主要在集群管理、资源隔离、用户main方法执行位置几个方面有所不同。
本篇会按照下面几个步骤进行介绍:
1 什么是Session模式
2 什么是Per Job模式
3 从任务解析过程到Application的设计初衷
4 什么是Application模式
5 启动过程源码分析
6 总结与参考资料
Session 模式
Flink支持事先创建好一个集群,然后往这个集群上提交任务。所有的任务都在客户端进行编译,编译成JobGraph后,附加上依赖的库,提交到Flink的集群。集群接收到任务后,会再创建对应的JobMaster进行ExecutionGraph的解析,然后申请资源并执行。如果Flink集群申请的TM内部有很多Slot,那么会按照Slot的粒度进行任务分配,这样就可能在一个TM上运行多个任务。
这样设计的好处是,多个任务可以共用一套集群,方便管理监控。但是带来的缺点也很明显,当某一个任务崩溃高挂了对应的TM,上面其他的任务都会受到影响。其他的任务受影响崩溃不说,如果大面积的任务恢复,也可能导致JM的性能压力。
因此Session模式适用于量多、执行任务时间短、对资源不敏感的场景,比如作为在线(即席)查询引擎。
关于Session模式的部署和使用,也可以参考之前的文章:
Flink Sql-Gateway在Yarn Session模式下的工作原理
Per job 模式
为了进行更好的资源隔离,Flink支持为每个任务单独创建一个集群,该模式目前支持Yarn、K8s等。当任务执行完毕,集群会自动关闭并回收资源。这样就保证了更好的资源隔离,单独的任务失败也不会影响其他的任务。另外,这种模式分摊了JM的压力到每个任务,因此这种模式更适合生产环境部署。
观察下图可以发现,per job模式和session模式,只有提交任务和启动graph不一样,其他后面的流程都是一样的。
因此Per Job模式适用于执行任务长、对资源敏感或者消耗资源大的任务。
从任务解析过程到Application的设计初衷
在Flink 1.11之前仅有上面两种模式,那么新实现的Application模式又是什么呢?在了解Application的由来时,最好先来了解下Flink程序的执行过程。
以DataStream API的程序为例,我们编写的.map().print()属于程序代码,对应上图的program code:
1 当执行env.execute()时,会触发程序代码编译成StreamGraph,StreamGraph主要的作用就是把.map、.partition等翻译成数据流图中的节点和边。
2 接下来任务提交前,会把StreamGraph编译成JobGraph,JobGraph更像是可以执行的图结构,并会对其中的一些节点进行合并优化,也叫做chain。比如输入数据后进行map操作,就可以在一个节点中同时完成读取和map操作。
3 生成JobGraph后,再把需要的依赖资源,如第三方Jar等一起提交到集群。
4 提交到集群后,session和job模式有所不同。session模式已经存在一个集群,此时的提交是直接发送到集群的dispatcher,内部创建对应的JobMaster,编译成ExecutionGraph。如果是per job模式则需要新建一个集群,等服务启动后,把附加过来的jobGraph直接用内部的dispatcher启动。他们的俩的区别简单来说,就是一个是事先创建好的集群,一个是临时启动的集群。
5 说回到ExecutionGraph,它就是常说的执行图,执行图代表了真正物理执行的拓扑图,比如并行的节点有多少;每个节点读取什么数据,从哪里读取;每个节点输出什么数据,输出到哪里;然后JobMaster通过调度器进行任务分配。
6 申请好的TM内部会有很多Slot,每个Slot接收发来的Task进行执行,直到任务结束。
7 任务结束后,Session模式会释放任务申请的资源,并通知内部的ResourceManager组件,方便后续来任务继续执行;Per Job模式会直接释放集群。
可以发现,无论是Session还是Per Job,程序代码都是在客户端编译完成。这里的客户端就是我们执行flink run启动的程序(其实是CliFrontend)。假如现在需要做一个平台给多个用户提交任务,或者任务的量级很大,那么客户端的压力会非常大。因为编译生成StreamGraph和JobGraph需要消耗大量的CPU,下载依赖的Jar包资源、上传JobGraph也需要大量的网络带宽,客户端很容易成为瓶颈。此时,就考虑可不可以把编译图的工作放在集群中完成?就类似于Spark的cluster模式,这就是Appllication模式。
Application 模式
Application的设计跟per job非常像,只不过客户端不在编译图,而是直接把执行的Jar和参数信息发送到yarn的AppMaster,在该进程中,同时完成JM的启动、编译图(用户main方法执行)、任务执行等过程。
这样还带来了其他的好处,比如一些公共的lib可以直接存储在Hdfs,避免多次上传下载浪费流量。
以Yarn部署为例,想要启动application模式,可以使用下面的命令:
# 基于application模式启动本地jar./bin/flink run-application -t yarn-application
./examples/batch/WordCount.jar
# 附加集群参数配置
./bin/flink run-application -t yarn-application
-Djobmanager.memory.process.size=2048m
-Dtaskmanager.memory.process.size=4096m
./examples/batch/WordCount.jar
# 基于application模式启动远程jar
./bin/flink run-application -t yarn-application
-Dyarn.provided.lib.dirs="hdfs://myhdfs/my-remote-flink-dist-dir"
hdfs://myhdfs/jars/my-application.jar
启动过程源码分析
观察flink脚本,可以看到 exec … org.apache.flink.client.cli.CliFrontend “$@“ 的命令,这就是客户端代码入口。
在run中是正常session和job的启动流程,在runApplication中为application模式启动流程。
在run中通过反射直接运行用户代码的main函数,在用户代码的execute()方法中编译图并提交到yarn。如果是session则直接发送给dispatcher,如果是per job则重新创建集群。
在Application中直接创建远程集群,并附加Application相关参数:
目前提交到集群启动的Master进程大致可以分为下面几种,后续会详细探索下各个Entrypoint中的细节。
总结
在Session模式中,集群的生命周期与任务无关,可以在集群中同时提交多个任务,他们共享集群资源。Per job模式中,每个任务单独维护集群,可以做到更好的资源隔离,集群的生命周期与任务相同。在Application模式中,为每个应用创建一个集群,main方法会运行在集群中,避免客户端过大的压力。
参考
Flink 1.11 官方文档——集群与部署:
https://ci.apache.org/projects/flink/flink-docs-release-1.11/ops/deployment/
Flink 1.11 官方文档——Yarn集群与部署:
https://ci.apache.org/projects/flink/flink-docs-release-1.11/ops/deployment/yarn_setup.html#user-jars--classpath
Flink 1.11 官方文档——CLI客户端命令:
https://ci.apache.org/projects/flink/flink-docs-release-1.11/ops/cli.html
FLIP-85 Application Mode:
https://cwiki.apache.org/confluence/display/FLINK/FLIP-85+Flink+Application+Mode
关于Application模式的邮件讨论:
http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/DISCUSS-FLIP-85-Delayed-Job-Graph-Generation-td35759.html
[简书]Flink 1.11 中的Application模式:
https://www.jianshu.com/p/85f2b32186cb
- 我所理解的RESTful Web API [设计篇]
- 黑箱难题阻碍了深度学习的普及与发展
- iOS 转场动画探究(一)
- XCode中如何使用事务
- 如何部署编译NDIS驱动的环境(内部资料)
- 深度学习的入门级装机配置推荐
- Self Host模式下的ASP. NET Web API是如何进行请求的监听与处理的?
- GridView绑定小技
- XCode读取Excel数据(适用于任何数据库)
- ObjectDataSource选择业务对象列表为空的探讨
- ASP.NET Web API自身对CORS的支持: CORS授权检验的实施
- 模版引擎XTemplate与代码生成器XCoder(源码)
- 深度学习让人脸识别准确率不断提升
- 在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- iOS音视频接入 - TRTC多人音视频通话
- Android平台RTMP推流或轻量级RTSP服务(同屏或摄像头)编码前数据接入类型总结
- 接口测试框架实战(二) | 搞定多环境下的接口测试
- MySQL 案例:“丢失数据”的谜题
- 接口测试框架实战(三) | APIObject 模式、原则与应用
- 接口测试框架实战(四) | 通用 API 封装实战
- 面试字节两轮后被完虐,一份字节跳动面试官给你的Android技术面试指南,请查收!
- 3分钟短文:说说Laravel模型中还算常用的2个“关系”
- iOS音视频接入 - TRTC实时屏幕分享
- 如何维护爬虫代理
- LoRaWAN 帧计数机制及典型问题分析
- ffmpeg mp4解码管道输出的问题
- 机器人运动控制仿真:Matlab机器人工具箱和Simmechanics
- 使用HTMLTestRunner实现HTML测试报告
- Jmeter五步实现性能测试