Flink笔记(二):架构解析

一、Flink整体架构

Flink整体架构可以分为APIs&Libraries、Core和Deploy三层:

  • Libraries层也被称作Flink应用组件层,是在API层之上构建满足了特定应用领域的计算框架,包括面向流处理的CEP(复杂事件处理)、类SQL操作,面向批处理的FlinkML(机器学习库)、Gelly(图处理)等;APIs层主要实现了面向流处理对应的DataStream API,面向批处理对应的DataSet API。
  • Core层提供了Flink运行时的全部核心实现,例如支持分布式Stream作业执行、JobGraph到ExecutionGraph的映射和调度等,为API层提供了基础服务。
  • Deploy层支持多种部署模式,包括本地、集群(Standalone、YARN、Kubernetes)及云部署(GCE/EC2)。
整体架构

接下来我们从上向下,依次介绍Flink的架构设计与实现,如有不当之处欢迎交流与拍砖~

二、Flink API

Flink提供了多种抽象的编程接口,适用于不同层级用户的需求,如下图所示:

Flink API

2.1 Stateful Processing Function

最底层级的抽象提供了强大且灵活的编程能力,在其中可以直接操作状态数据、TimeService等服务,同时可以注册事件时间和处理时间回调定时器,使程序能够实现更加复杂的计算。使用Stateful Processing Function需要借助DataStream API。虽然灵活度很高,但是使用复杂度也相对较高,且在DataStreamAPI中已经封装了丰富的算子可以直接使用,因此除非用户需要自定义比较复杂的算子(如直接操作状态数据等),否则无须使用Stateful Processing Function来开发Flink作业。

2.2 DataStream&DataSet API

大多数应用都是针对Core API进行编程 :DataStream API( 有界或无界流数据) 和DataSet API(有界数据集)。这些 API提供了通用的数据处理操作, 比如由用户定义的多种形式的转换( transformations)、连接( joins)、聚合( aggregations)、窗口操作( windows) 等等。DataSet API 为有界数据集提供了额外的支持, 例如循环与迭代。

值得一提的是,虽然Table和SQL API已经能够做到批流一体,但这仅是在逻辑层面上,最终还是会转换成DataSet API和DataStream API对应的作业。在未来的版本中,Flink将逐渐通过DataStream处理有界数据集和无界数据集,实现真正意义上的批流一体。

2.3 Flink SQL & Table API

Flink提供的高层级的抽象是Table API与Flink SQL 。其中Table API 是以表为中心的声明式编程,表可能会动态变化(在表达流数据时)。Table API遵循关系模型:表有二维数据结构(schema,类似于关系数据库中的表),同时API提供常用的查询操作,例如select、project、join、group-by、aggregate等。Table API 可以通过用户自定义函数( UDF)进行扩展。除此之外,Table API程序在执行之前会经过内置优化器进行优化。

Flink SQL在语法与表达能力上与Table API类似,只是以SQL查询表达式的形式编写和表达逻辑。SQL抽象与Table API交互密切,SQL查询可以直接在Table API定义的表上执行。

三、DataStream解析

DataStream API用于构建流式类型的Flink程序,处理实时无界数据流,是Flink系统中最重要的API。本节结合一个简单的示例对DataStream API进行浅析。

3.1 WordCount示例

// set up the execution environment
final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

// make parameters available in the web interface
env.getConfig().setGlobalJobParameters(params);

// get input data
DataStream<String> dataStream = env.readTextFile("the_path_for_input");
DataStream<Tuple2<String, Integer>> counts =
        // split up the lines in pairs (2-tuples) containing: (word,1)
        dataStream.flatMap(new Tokenizer())
                // group by the tuple field "0" and sum up tuple field "1"
                .keyBy(value -> value.f0)
                .sum(1);

counts.print();

// execute program
env.execute("Streaming WordCount");

从上面WordCount代码示例可以看出,一个Flink流处理程序主要包括以下3个部分:

  1. StreamExecutionEnvironment初始化:该部分主要创建和初始化StreamExecutionEnvironment,提供通过DataStream API构建Flink作业需要的执行环境,包括设定ExecutionConfig、CheckpointConfig等配置信息以及StateBackend和TimeCharacteristic等变量。
  2. 业务逻辑转换代码:该模块是用户编写转换逻辑的区域,在streamExecutionEnvironment中提供了创建DataStream的方法,例如通过StreamExecutionEnvironment.readTextFile()方法读取文本数据并构建DataStreamSource数据集,之后所有的DataStream转换操作都会以DataStreamSource为头部节点。同时,DataStreamAPI中提供了各种转换操作,例如map、reduce、join等算子,用户可以通过这些转换操作构建完整的Flink计算逻辑。
  3. 执行应用程序:编写完Flink应用后,必须调用ExecutionEnvironment.execute()方法执行整个应用程序,在execute()方法中会基于DataStream之间的转换操作生成StreamGraph,并将StreamGraph结构转换为JobGraph,最终将JobGraph提交到指定的Session集群中运行。

3.2 DataStream结构

DataStream数据结构包含两个主要成员:streamExecutionEnvironment和transformation。DataStream用于表达业务转换逻辑,可以通过transformation生成新的DataStream。DataStream实际上并不存储真实数据。

DataStream

如上图所示,DataStream之间的转换操作都是通过StreamTransformation进行的,例如当用户执行DataStream.map()方法转换时,底层对应的便是OneInputTransformation转换操作。在DataStream转换的过程中,不管是哪种类型的转换操作,都是按照相同方式进行:首先将用户自定义的函数(如示例中的new Tokenizer())封装到Operator中,然后将Operator封装到Transformation转换操作结构中,最后将Transformation写入StreamExecutionEnvironment提供的Transformation集合。通过DataStream之间的转换操作形成Pipeline拓扑,即StreamGraph数据结构,最终通过StreamGraph生成JobGraph并提交到集群上运行。

3.3 Transformation

由上文可以知道,DataStream之间的转换操作都是基于Transformation来实现的,每种Transformation实现都和DataStream的一个接口方法对应。

Transformation类图

从上面的的Transformation类图可以看出,Transformation的子类涵盖了所有的DataStream转换操作。常用到的StreamMap、StreamFilter算子封装在OneInputTransformation中,即单输入类型的转换操作;常见的双输入类型算子有join、connect等,对应支持双输入类型转换的TwoInputTransformation。

在Transformation的基础上又抽象出了PhysicalTransformation类。PhysicalTransformation中提供了setChainingStrategy方法,可以将上下游算子按照指定的策略连接,从而减少网络数据传输、提高计算性能。ChainingStrategy支持如下三种策略:

  • ALWAYS:代表该Transformation中的算子会和上游算子尽可能地链化,最终将多个Operator组合成OperatorChain。OperatorChain中的Operator会运行在同一个SubTask实例中,这样做的目的主要是优化性能,减少Operator之间的网络传输。
  • NEVER:代表该Transformation中的Operator永远不会和上下游算子之间链化,因此对应的Operator会运行在独立的SubTask实例中。
  • HEAD:代表该Transformation对应的Operator为头部算子,不支持上游算子链化,但是可以和下游算子链化,实际上就是OperatorChain中的HeaderOperator。

这里整理了常用的Transformation,如下图所示:

Transformation

四、运行时架构

Flink客户端会将用户的作业转换为JobGraph结构并提交至集群的运行时中,对作业进行调度并拆分成Task继续调度和执行。运行时中的核心组件和服务会分工并协调合作,最终完成整个Job的调度和执行。

运行时架构

4.1 主要组件

这里先介绍几个Flink中的重要概念:

  • Job: 一个Job对应一个用户提交的作业,也即对应一个jobGraph
  • Task: Flink会基于jobGraph中每个算子的链化策略(见3.3)和用户设置的并发度,将一个Job拆分为多个Task,并为每个Task申请资源执行
  • Slot: 是Flink中并发执行的最小单位,可以理解为Java中的线程。Slot由集群中的TaskManager提供、ResourceManager统一管理。

Dispatcher

Dispatcher主要负责接收客户端提交的JobGraph对象(例如CLI客户端或FlinkWebUI提交的任务最终都会发送至Dispatcher组件),并对JobGraph进行分发和执行。其中就包含根据JobGraph对象启动JobManager服务,专门用于管理整个任务的生命周期。

ResourceManager

ResourceManager有两个主要职责:负责管理Flink集群中的计算资源,其中计算资源主要来自TaskManager组件;以及接收来自JobManager的SlotRequest。

如果采用集群部署方式,则ResourceManager会动态地向集群资源管理器申请Container并启动TaskManager,例如HadoopYarn、Kubernetes等。对于不同的集群资源管理器,ResourceManager的实现也会有所不同。

JobManager

Dispatcher会根据接收的JobGraph对象为任务创建JobManager服务,由后者对整个任务的生命周期进行管理。JobManager会将JobGraph转换成ExecutionGraph结构,并通过内部调度程序对ExecutionGraph中的ExecutionVertex节点进行调度和执行,最终会经过ResourceManager向指定的TaskManager提交和运行Task实例。同时也会监控各个Task的运行状况,直到整个作业中所有的Task都执行完毕或停止。

和Dispatcher组件一样,JobManager组件本身也是RPC服务,因此具备RPC通信的能力,可以与ResourceManager进行RPC通信、申请任务的计算资源。当任务执行完毕后,JobManager服务也会关闭并释放任务占用的计算资源。

TaskManager

TaskManager负责向整个集群提供Slot计算资源、并管理JobManager提交的Task任务。TaskManager会向JobManager服务提供从ResourceManager中申请和分配的Slot计算资源,JobManager最终会根据分配到的Slot计算资源将Task提交到TaskManager上运行。

4.2 执行流程

接下来我们看整个集群中各个主要组件的启动流程。如上图,我们以Session类型(见第五节)的集群为例进行说明,Flink Session集群的启动流程主要包含如下步骤:

  1. 用户通过客户端命令启动SessionCluster,此时会触发整个集群服务的启动过程,客户端会向集群资源管理器申请Container计算资源以启动运行时中的管理节点。
  2. ClusterManagement会为运行时集群分配Application主节点需要的资源并启动主节点服务,例如在HadoopYarn资源管理器中会分配并启动Flink管理节点对应的Container。
  3. 客户端将用户提交的应用程序代码经过本地运行生成JobGraph结构,然后通过ClusterClient将JobGraph提交到集群运行时中运行。
  4. 此时集群运行时中的Dispatcher服务会接收到ClusterClient提交的JobGraph对象,然后根据JobGraph启动JobManagerRPC服务。JobManager是每个提交的作业都会单独创建的作业管理服务,生命周期和整个作业的生命周期一致。
  5. 当JobManagerRPC服务启动后,下一步就是根据JobGraph配置的计算资源向ResourceManager服务申请运行Task实例需要的Slot计算资源。
  6. 此时ResourceManager接收到JobManager提交的资源申请后,先判断集群中是否有足够的Slot资源满足作业的资源申请,如果有则直接向JobManager分配计算资源,如果没有则动态地向外部集群资源管理器申请启动额外的Container以提供Slot计算资源。
  7. 如果在集群资源管理器(例如HadoopYarn)中有足够的Container计算资源,就会根据ResourceManager的命令启动指定的TaskManager实例。
  8. TaskManager启动后会主动向ResourceManager注册Slot信息,即其自身能提供的全部Slot资源。ResourceManager接收到TaskManager中的Slot计算资源时,就会立即向该TaskManager发送Slot资源申请,为JobManager服务分配提交任务所需的Slot计算资源。
  9. 当TaskManager接收到ResourceManager的资源分配请求后,TaskManager会对符合申请条件的SlotRequest进行处理,然后立即向JobManager提供Slot资源。
  10. 此时JobManager会接收到来自TaskManager的offerslots消息,接下来会向Slot所在的TaskManager申请提交Task实例。TaskManager接收到来自JobManager的Task启动申请后,会在已经分配的Slot卡槽中启动Task线程。
  11. TaskManager中启动的Task线程会周期性地向JobManager汇报任务运行状态,直到完成整个任务运行。

五、Flink部署模式

常见的Flink部署方式有如下三种:

  • Standalone:单机部署
  • Flink on YARN:YARN集群部署
  • Flink on Kubernetes:容器化部署

这里以Flink on YARN来说明两种常用的部署模式:

  • Session-Cluster模式:Session-Cluster模式需要先启动集群,然后再提交作业,接着会向YARN申请一块空间后,资源永远保持不变。如果资源满了,下一个作业就无法提交,只能等到YARN中的其中一个作业执行完成后释放了资源,下个作业才会正常提交。所有作业共享Dispatcher和ResourceManager、共享资源,适合规模小执行时间短的作业。
Session-Cluster模式
  • Per-Job-Cluster模式:一个Job会对应一个集群,每提交一个作业会根据自身的情况,都会单独向YARN申请资源,直到作业执行完成,一个作业的失败与否并不会影响下一个作业的正常提交和运行。独享Dispatcher和ResourceManager,按需接受资源申请,适合规模大长时间运行的作业。
Per-Job-Cluster

参考资料

  • Flink源码
  • 《Flink设计与实现 核心原理与源码解析》--张利兵
  • B站Flink教程
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270