Flink架构,功能快速介绍

Flink简介

Flink核心是一个流式的数据流执行引擎,其针对数据流的分布式计算提供了数据分布、数据通信以及容错机制等功能。基于流执行引擎,Flink提供了诸多更高抽象层的API以便用户编写分布式任务:

  1. DataSet API: 对静态数据进行批处理操作,支持Java、Scala和Python。
  2. DataStream API:对数据流进行流处理操作,支持Java和Scala。
  3. Table API:对结构化数据进行查询操作,将结构化数据抽象成关系表,并通过类SQL的DSL对关系表进行各种查询操作,支持Java和Scala。

它还集成了以下三种类型的库,以支持更复杂的应用:

  1. CEP,复杂的事件处理库(complex event processing library)
  2. Machine Learning library
  3. Gelly, 图处理库
    在这里插入图片描述

统一的批处理与流处理系统

数据集分为有界数据集和无界数据集:

  1. 有界数据集:某个时间区间内(比如全表),批处理;
  2. 无界数据集:源源不断(比如日志),流处理。

有限流处理是无限流处理的一种特殊情况,它只不过在某个时间点停止而已。此外,如果计算结果不在执行过程中连续生成,而仅在末尾处生成一次,那就是批处理(分批处理数据)。
通过其灵活的执行引擎,Flink能够同时支持批处理任务与流处理任务。
在执行引擎这一层,流处理系统与批处理系统最大不同在于节点间的数据传输方式。对于一个流处理系统,其节点间数据传输的标准模型是:当一条数据被处理完成后,序列化到缓存中,然后立刻通过网络传输到下一个节点,由下一个节点继续处理。
而对于一个批处理系统,其节点间数据传输的标准模型是:当一条数据被处理完成后,序列化到缓存中,并不会立刻通过网络传输到下一个节点,当缓存写满,就持久化到本地硬盘上,当所有数据都被处理完成后,才开始将处理后的数据通过网络传输到下一个节点。
这两种数据传输模式是两个极端,对应的是流处理系统对低延迟的要求和批处理系统对高吞吐量的要求。
Flink的执行引擎采用了一种十分灵活的方式,同时支持了这两种数据传输模型。Flink以固定的缓存块为单位进行网络数据传输,用户可以通过缓存块超时值指定缓存块的传输时机。
如果缓存块的超时值为0,则Flink的数据传输方式类似上文所提到流处理系统的标准模型,此时系统可以获得最低的处理延迟。
如果缓存块的超时值为无限大,则Flink的数据传输方式类似上文所提到批处理系统的标准模型,此时系统可以获得最高的吞吐量。同时缓存块的超时值也可以设置为0到无限大之间的任意值。缓存块的超时阈值越小,则Flink流处理执行引擎的数据处理延迟越低,但吞吐量也会降低,反之亦然。通过调整缓存块的超时阈值,用户可根据需求灵活地权衡系统延迟和吞吐量。

Flink优势

具体的优势有以下几点

  1. 同时支持高吞吐、低延迟、高性能
    是目前开源社区中唯一一套集高吞吐、低延迟、高性能三者于一身的分布式流式数据处理框架。
    像 Apache Spark 也只能兼顾高吞吐和高性能特性,无法做到低延迟保障
    Apache Storm 只能支持低延时和高性能特性,无法满足高吞吐的要求
  2. 支持事件时间(Event Time)概念
    在流式计算领域中,窗口计算的地位举足轻重,但目前大多数框架窗口计算采用的都是系统时间(Process Time),也是事件传输到计算框架处理时,系统主机的当前时间。
    Flink 能够支持基于事件时间(Event Time)语义进行窗口计算,这种基于事件驱动的机制使得事件即使乱序到达,流系统也能够计算出精确的结果,保持了事件原本产生时的时序性,尽可能避免网络传输或硬件系统的影响。
  3. 支持有状态计算
    所谓状态就是在流式计算过程中将算子的中间结果保存在内存或者文件系统中,等下一个事件进入算子后可以从之前的状态中获取中间结果,计算当前的结果,从而无须每次都基于全部的原始数据来统计结果,极大的提升了系统性能
  4. 支持高度灵活的窗口(Window)操作
    Flink 将窗口划分为基于 Time 、Count 、Session、以及Data-Driven等类型的窗口操作,窗口可以用灵活的触发条件定制化来达到对复杂的流传输模式的支持,用户可以定义不同的窗口触发机制来满足不同的需求
  5. 基于轻量级分布式快照(Snapshot)实现的容错
    Flink 能够分布运行在上千个节点上,通过基于分布式快照技术的Checkpoints,将执行过程中的状态信息进行持久化存储,一旦任务出现异常停止,Flink 能够从 Checkpoints 中进行任务的自动恢复,以确保数据爱处理过程中的一致性
  6. 基于 JVM 实现的独立的内存管理
    Flink 实现了自身管理内存的机制,尽可能减少 JVM GC 对系统的影响。
    通过序列化/反序列化机制将所有的数据对象转换成二进制在内存中存储,降低数据存储大小的同时,更加有效的利用空间,降低GC带来的性能下降或任务异常的风险
  7. Save Points 保存点
    对于 7 * 24 小时运行的流式应用,数据源源不断的流入,在一段时间内应用的终止有可能导致数据的丢失或者计算结果的不准确。
    比如集群版本的升级,停机运维操作等。
    值得一提的是,Flink 通过Save Points 技术将任务执行的快照保存在存储介质上,当任务重启的时候,可以从事先保存的 Save Points 恢复原有的计算状态,使得任务继续按照停机之前的状态运行。

Flink数据流编程模型

抽象层次

Flink提供不同级别的抽象来开发流/批处理应用程序。
在这里插入图片描述

  • Flink API 最底层的抽象为有状态实时流处理。其抽象实现是 Process Function,并且 Process Function 被 Flink 框架集成到了 DataStream API 中来为我们使用。它允许用户在应用程序中自由地处理来自单流或多流的事件(数据),并提供具有全局一致性和容错保障的状态。此外,用户可以在此层抽象中注册事件时间(event time)和处理时间(processing time)回调方法,从而允许程序可以实现复杂计算。

  • Flink API 第二层抽象是 Core APIs。实际上,许多应用程序不需要使用到上述最底层抽象的 API,而是可以使用 Core APIs 进行编程:其中包含 DataStream API(应用于有界/无界数据流场景)和 DataSet API(应用于有界数据集场景)两部分。Core APIs 提供的流式 API(Fluent API)为数据处理提供了通用的模块组件,例如各种形式的用户自定义转换(transformations)、联接(joins)、聚合(aggregations)、窗口(windows)和状态(state)操作等。此层 API 中处理的数据类型在每种编程语言中都有其对应的类。

  • Flink API 第三层抽象是 Table API。Table API 是以表(Table)为中心的声明式编程(DSL)API,例如在流式数据场景下,它可以表示一张正在动态改变的表。Table API 遵循(扩展)关系模型:即表拥有 schema(类似于关系型数据库中的 schema),并且 Table API 也提供了类似于关系模型中的操作,比如 select、project、join、group-by 和 aggregate 等。Table API 程序是以声明的方式定义应执行的逻辑操作,而不是确切地指定程序应该执行的代码。尽管 Table API 使用起来很简洁并且可以由各种类型的用户自定义函数扩展功能,但还是比 Core API 的表达能力差。此外,Table API 程序在执行之前还会使用优化器中的优化规则对用户编写的表达式进行优化。

    表和 DataStream/DataSet 可以进行无缝切换,Flink 允许用户在编写应用程序时将 Table API 与 DataStream/DataSet API 混合使用。

  • Flink API 最顶层抽象是 SQL。这层抽象在语义和程序表达式上都类似于 Table API,但是其程序实现都是 SQL 查询表达式。SQL 抽象与 Table API 抽象之间的关联是非常紧密的,并且 SQL 查询语句可以在 Table API 中定义的表上执行。

程序和数据流

Flink 程序是由 streams 和 transformations(也可以叫做 operator,在 Flink 的实现中 operator 被封装在 transformation 里)构成的,stream 可以认为是数据的中间结果,而 transformation 是对一个或多个 input stream 的操作,transformation 可以输出一个或多个 out stream。
当任务被执行后,Flink 程序被映射为 streaming dataflow,每个 dataflow 由多个 streams 和 transformation operations 组成。每个 dataflow 描述了从一个或多个 sources 经由各种 transformation,最终到一个或多个 sinks(类似 DAG)。
大多数情况下,transformation 和 source/sink operator 之间是 one-to-one 的,但是,一个 transformation 也可以由多个 transformation operations 组成。
在这里插入图片描述

并行数据流

Flink 支持并行(Parallel)与分布式,在 Flink 中,streams 被拆分为 stream partitions,operators 被拆分为 operator subtasks。同一个 operator 下的 operator 4 subtask 可以独立的运行在不同服务器或 containers 的不同线程上。
在这里插入图片描述streams 可以在两个 operators 中传输数据,传输的方式:one-to-one, redistributing。

  1. One-to-one stream: 以上图的 source operator 和 map operator 为例, one-to-one stream 保存着数据的 partitioning 和数据的顺序,这就意味着map[1]这个 operator subtask 能够相同顺序下的相同的元素,这是因为这 些元素都是都是由 source[1]这个 operator subtask 来 input 的。
  2. Redistributing stream: 以上图 map operator 以及之后的逻辑为例, redistributing stream 改变了 streams 的 partitioning。根据已选的transformation,每个 operator subtask 发送数据到不同的 target subtasks 上,在 redistributing exchange 中,数据的顺序只被保存 数据元之间的排序仅保存在每对发送和接收子任务中(例如,map()的子任 务[1] 和子任务[2]keyBy / window)。

窗口

聚合计算在流式处理与批式处理有很大的不同,因为流是没有边界的,我们 无法拿到所有的数据做运算并得出结果。因此,为了解决在流式系统中的数据的聚合计算,Flink 引入窗口的概念。流上面的聚合操作(count, sum 等)会被 scope 成一个个 window。例如“在最后5分钟内计数”或“最后100个数据元的总和”。
窗口可以分为时间驱动的窗口 (tumbling window(没有重叠) 、 sliding window(有重叠))和session windows(由不活动的间隙打断)。
在这里插入图片描述

时间

Flink 中,时间有以下 3 类:

  • Event time: 事件的发生时间,flink 通过 timestamp assigners 获得 event time;
  • Ingestion time: 事件进入 flink 的 source operator 的时间;
  • Processing time: 每个 operator 处理事件的时间;
    在这里插入图片描述Flink使用WaterMark衡量时间的时间,WaterMark携带时间戳t,并被插入到stream中。
  • WaterMark的含义是所有时间t’< t的事件都已经发生。
  • 针对乱序的的流,WaterMark至关重要,这样可以允许一些事件到达延迟,而不至于过于影响window窗口的计算。
  • 并行数据流中,当Operator有多个输入流时,Operator的event time以最小流event time为准。
    在这里插入图片描述

状态管理

在 Flink 中,有的 operations 在 dataflow 中只需要关注一个事件信息(例如: event parser),有的 operators 需要记住多个不同的 event 的信息(比如 window operator),这类的 operators 被称为 stateful operators。
在这里插入图片描述
在Flink,state总是和特定的operator关联。operator需要注册它的state,而state有两种类型:

  1. Operator State:由同一并行任务处理的所有记录都可以访问相同的state,而其他的task或operator不能访问,即一个task专属一个state。这种state有三种primitives
  • List State represents state as a list of entries.
  • Union List State同上,但在任务失败和作业从savepoint重启的行为不一样
  • Broadcast State(v1.5) 同样一个task专属一个state,但state都是一样的(需要自己注意保持一致,对state更新时,实际上只对当前task的state进行更新。只有所有task的更新一样时,即输入数据一样(一开始广播所以一样,但数据的顺序可能不一样),对数据的处理一样,才能保证state一样)。这种state只能存储在内存,所以没有RockDB backend。
  1. Keyed State:相同key的record共享一个state。
  • Value State:每个key一个值,这个值可以是复杂的数据结构.
  • List State:每个key一个list
  • Map State:每个key一个map

Flink架构

Flink集群

Flink 的运行时有两类进程:

  • Master 进程:也叫作 JobManagers,它们主要用于协同分布式执行,比如 schedule tasks,coordinate checkpoints,coordinate recovery on failures 等。一个集
    群中至少有一个 master,高可用的分布式集群拥有多个 master 进程(其中一个是 leader, 其他的是 standby)。
  • Worker 进程:也叫作 TaskManagers,他们用来执行 dataflow 的 tasks、缓冲 和交换 data streams。Worker 进程至少要有一个。

Master 进程和 worker 进程可以被随意的启动,它们可以直接在服务器上启动,也可以在 YARN 上启动。Worker 进程主动连接 Master 进程,告诉 Master 进程他们是可用的。
在这里插入图片描述

任务和算子链

在分布式的执行环境中,Flink 将 operator subtasks 链成 task。每个 task 由一 个线程执行。将 operator 链成 task 可以减少线程的切换、buffering,提高了整个程序的吞吐量。operator subtasks 链成 task 在 API 中配置。
在这里插入图片描述

TaskSlot和资源

每个 worker 是一个 JVM 进程,它可以在不同的线程中执行一个或多个 subtasks。Worker 进程中的 task slots 用于控制一个 worker 进程能够接收多少 tasks。
每个 task slot 代表了 TaskManager 中的固定的资源(的子集)。举个例子, 如果一个 TaskManager 拥有 3 个 task slots,那么它会把它占有的内存平均划分成3 份分别分配给每个 task slot。Sloting resources 意味着某个 subtask 不能够在其他 job 的内存中运行,而是在固定的内存中运行。这样可以保证不同的 task slot 中 的 subtask 不会相互竞争资源。注意:CPU 不会被分配,只有内存会被分配。
通过调整 task slots 的数量,可以调整 subtasks 之间的隔离程度。如果每个 TaskManager 只有一个 task slot,那么每个 task 都会运行在独立的 JVM 中。如果TaskManager 有多个 task slots 的时候, 这就意味着将会有多个 subtasks 将会运 行在同一个 JVM 中。在相同 JVM 中运行的 subtasks 能够共享 TCP connections 和心跳信息,它们还可以共享数据集和数据结构, 从而节约每个task的开销。
在这里插入图片描述
默认情况下,Flink允许subtask共享slot,即使它们是不同task的subtask,只要它们来自同一个job。结果是一个slot可以保存job的整个pipline。允许slot共享有两个主要好处:

  • Flink集群需要与作业中使用的最高并行度一样多的solt。无需计算程序总共包含多少task(具有不同的并行性)。
  • 更容易获得更好的资源利用率。如果没有slot sharing,非密集source/map() subtask将使用和密集型window subtask一样多的资源。通过slot sharing,将示例中的 基本并行性从2增加到6可以充分利用时隙资源,同时确保繁重的subtask在TaskManagers之间公平分配。
    在这里插入图片描述

执行图

Flink 中的执行图可以分成四层:StreamGraph -> JobGraph -> ExecutionGraph -> 物理执行图。

  • StreamGraph:是根据用户通过 Stream API 编写的代码生成的最初的图。用来表示程序的拓扑结构。

  • JobGraph:StreamGraph经过优化后生成了 JobGraph,提交给 JobManager 的数据结构。主要的优化为,将多个符合条件的节点 chain 在一起作为一个节点,这样可以减少数据在节点之间流动所需要的序列化/反序列化/传输消耗。

  • ExecutionGraph:JobManager 根据 JobGraph 生成ExecutionGraph。方便调度和监控和跟踪各个 tasks 的状态。ExecutionGraph是JobGraph的并行化版本,是调度层最核心的数据结构。

  • 物理执行图:JobManager 根据 ExecutionGraph 对 Job 进行调度后,在各个TaskManager 上部署 Task 后形成的“图”,并不是一个具体的数据结构。

  • 2个并发度(Source为1个并发度)的 SocketTextStreamWordCount 四层执行图的演变过程:
    在这里插入图片描述

  • StreamGraph:根据用户通过 Stream API 编写的代码生成的最初的图。

    • StreamNode:用来代表 operator 的类,并具有所有相关的属性,如并发度、入边和出边等。
    • StreamEdge:表示连接两个StreamNode的边。
  • JobGraph:StreamGraph经过优化后生成了 JobGraph,提交给 JobManager 的数据结构。

    • JobVertex:经过优化后符合条件的多个StreamNode可能会chain在一起生成一个JobVertex,即一个JobVertex包含一个或多个operator,JobVertex的输入是JobEdge,输出是IntermediateDataSet。
    • IntermediateDataSet:表示JobVertex的输出,即经过operator处理产生的数据集。producer是JobVertex,consumer是JobEdge。
    • JobEdge:代表了job graph中的一条数据传输通道。source 是 IntermediateDataSet,target 是 JobVertex。即数据通过JobEdge由IntermediateDataSet传递给目标JobVertex。
  • ExecutionGraph:JobManager 根据 JobGraph 生成ExecutionGraph。ExecutionGraph是JobGraph的并行化版本,是调度层最核心的数据结构。

    • ExecutionJobVertex:和JobGraph中的JobVertex一一对应。每一个ExecutionJobVertex都有和并发度一样多的 ExecutionVertex。
    • ExecutionVertex:表示ExecutionJobVertex的其中一个并发子任务,输入是ExecutionEdge,输出是IntermediateResultPartition。
    • IntermediateResult:和JobGraph中的IntermediateDataSet一一对应。一个IntermediateResult包含多个IntermediateResultPartition,其个数等于该operator的并发度。
    • IntermediateResultPartition:表示ExecutionVertex的一个输出分区,producer是ExecutionVertex,consumer是若干个ExecutionEdge。
    • ExecutionEdge:表示ExecutionVertex的输入,source是IntermediateResultPartition,target是ExecutionVertex。source和target都只能是一个。
    • Execution:是执行一个 ExecutionVertex 的一次尝试。当发生故障或者数据需要重算的情况下 ExecutionVertex 可能会有多个 ExecutionAttemptID。一个 Execution 通过 ExecutionAttemptID 来唯一标识。JM和TM之间关于 task 的部署和 task status 的更新都是通过 ExecutionAttemptID 来确定消息接受者。
  • 物理执行图:JobManager 根据 ExecutionGraph 对 Job 进行调度后,在各个TaskManager 上部署 Task 后形成的“图”,并不是一个具体的数据结构。

    • Task:Execution被调度后在分配的 TaskManager 中启动对应的 Task。Task 包裹了具有用户执行逻辑的 operator。
    • ResultPartition:代表由一个Task的生成的数据,和ExecutionGraph中的IntermediateResultPartition一一对应。
    • ResultSubpartition:是ResultPartition的一个子分区。每个ResultPartition包含多个ResultSubpartition,其数目要由下游消费 Task 数和 DistributionPattern 来决定。
    • InputGate:代表Task的输入封装,和JobGraph中JobEdge一一对应。每个InputGate消费了一个或多个的ResultPartition。
    • InputChannel:每个InputGate会包含一个以上的InputChannel,和ExecutionGraph中的ExecutionEdge一一对应,也和ResultSubpartition一对一地相连,即一个InputChannel接收一个ResultSubpartition的输出。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值