1. 简介

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

1.1.无界数据流与有界数据流

1.1.1.无界数据流

无界数据流有一个开始但是没有结束 , 它们不会在生成时终止并 提供数据,必须连续处理无界流,也就是说必须在获取后立即 处理 event 。对于无界 数据流我们无法等待所有数据都到达, 因为输入是无界的, 并且在任何时间点都不 会完成。处理无界数据通常要求以特定顺序(例如事件发生的顺序)获取 event ,以 便能够推断结果完整性 ,无界流的处理称为流处理 。

1.1.2.有界数据流

有界数据流有明确定义的开始和结束, 可以在执行任何计算之前 通过获取所有数据来处理有界流, 处理有界流不需要有序获取, 因为可以始终对有 界数据集进行排序, 有界流的处理也称为批处理。

1.2.批处理和流处理

批处理的特点是有界、持久、大量, 批处理非常适合需要访问全套记录才能完成的计算工作, 一般用于离线统计。

流处理的特点是无界、实时, 流处理方式无需针对整个数据集执行操作, 而是对通过系统传输的每个数据项执行操作 , 一般用于实时统计 。

在 Spark 生态体系中, 对于批处理和流处理采用了不同的技术框架,批处理由 Spark SQL 实现, 流处理由 Spark Streaming 实现, 这也是大部分框架采用的策略, 使用独立的处理器实现批处理和流处理, 而 Flink 可以同时实现批处理和流处理。

Apache Flink 是一个面向分布式数据流处理和批量数据处理的开源计算平台,能够基于同一个 Flink 运行时 (Flink Runtime) , 提供支持流处理和批处理两种类 型应用的功能 。 现有的开源计算 方案, 会把流处理和批处理作为两种不同的应用类 型,因为它们要实现的目标是完全不相同的:流处理一般需要支持低延迟、 Exactly-once 保证 , 而 批处理需要支持高吞吐、高效处理 , 所以在实现的时候通常 是分别给出两套实现方法, 或者通过一个独立的开源框架来实现其中每一种处理方 案。

Flink 是完全支持流处理,作为流处理时将输入数据流视为无界数据流 ; 批处理被作为一种特殊的流处理, 只是它的输入数据流被定义为有界的 。

Flink 是标准的实时流处理引擎,基于事件驱动。而 Spark Streaming 是微批[Micro-Batch的模型。

1.4.1.任务调度

Spark Streaming 连续不断的生成微小的数据批次,构建有向无环图 DAG,Spark Streaming 会依次创建 DStreamGraph、JobGenerator、JobScheduler。

Flink 根据用户提交的代码生成 StreamGraph,经过优化生成 JobGraph,然后提交给 JobManager进行处理,JobManager 会根据 JobGraph 生成 ExecutionGraph,ExecutionGraph 是 Flink 调度最核心的数据结构,JobManager 根据 ExecutionGraph 对 Job 进行调度。

1.4.2. 时间机制

Spark Streaming 支持的时间机制有限,只支持处理时间
Flink 支持了流处理程序在时间上的三个定义:处理时间、事件时间、注入时间。同时也支持 watermark 机制来处理滞后数据。

1.4.3. 容错机制

对于 Spark Streaming 任务,我们可以设置 checkpoint,然后假如发生故障并重启,我们可以从上次 checkpoint 之处恢复,但是这个行为只能使得数据不丢失,可能会重复处理,不能做到恰一次处理语义。

2.系统架构

当 Flink 集群启动后,首先会启动一个JobManger 和一个或多个的 TaskManager。由 Client 提交任务给 JobManager,JobManager 再调度任务到各个 TaskManager 去执行, TaskManager 将心跳和统计信息汇报给 JobManager。

Client

Client 为提交 Job 的客户端,可以是运行在任何机器上[与 JobManager 环境连通即可]。提交 Job 后,Client 可以结束进程,也可以不结束并等待结果返回。

Dispatcher

提供 REST 接口来接收 client 的 application 提交,它负责启动 TaskManager 和提交 application,同时运行 Web UI。

JobManager

JobManager 负责整个 Flink 集群任务的调度以及资源的管理,从客户端中获取提交的 Flink Job,然后根据集群中 TaskManager 上 TaskSlot 的使用情况,为提交的Job 分配相应的 TaskSlot 资源并命令 TaskManager 启动从客户端中获取的应用。

JobManager 相当于整个集群的 Master 节点,且整个集群有且只有一个活跃的 JobManager ,负责整个集群的任务调度和资源管理。

JobManager包含了3个重要的组件

Actor

JobManager 和 TaskManager 之间通过 Actor System 进行通信,获取任务执行的情况并通过 Actor System 将应用的任务执行情况发送给客户端。

调度

检查点

Flink的检查点机制是保证其一致性容错功能的骨架。它持续的为分布式的数据流和有状态的operator生成一致性的快照。Flink的容错机制持续的构建轻量级的分布式快照,因此负载非常低。通常这些有状态的快照都被放在HDFS中存储(state backend)。程序一旦失败,Flink将停止executor并从最近的完成了的检查点开始恢复(依赖可重发的数据源+快照)

TaskManager

TaskManager 相当于整个集群的 Slave 节点,负责具体的任务执行和对应任务在每个节点上的资源申请和管理。

客户端通过将编写好的 Flink 应用编译打包,提交到 JobManager,然后 JobManager 会根据已注册在 JobManager 中 TaskManager 的资源情况,将任务分配给有资源的 TaskManager节点,然后启动并运行任务。

TaskManager 从 JobManager 接收 Job然后使用 Slot 资源启动 Task,建立数据接入的网络连接,接收数据并开始数据处理。同时 TaskManager 之间的数据交互都是通过数据流的方式进行的。

可以看出,Flink 的任务运行其实是采用多线程的方式,这和 MapReduce 多 进程的方式有很大的区别,Flink 能够极大提高 CPU 使用效率,在多个任务和 Task之间通过 TaskSlot 方式共享系统资源,每个 TaskManager 中通过管理多个 TaskSlot 资源池进行对资源进行有效管理。

Slots

Flink 中每一个 TaskManager 都是一个 JVM 进程,他可能会在独立的线程上执行一个或多个 subtask

为了控制一个 TaskManager 能接收多少个 task,TaskManager通过 task slot 来进行控制, 一个TaskManager至少有一个 slot

  • Slot 共享

    默认情况下,Flink 允许 subtasks共享 slot

    条件是它们都来自同一个 Job 的不同 task 的 subtask。结果可能是一个 slot 持有该 job的整个pipeline。

    优点

    • Flink 集群需要的任务槽与作业中使用的最高并行度正好相同(前提,保持默认SlotSharingGroup)。也就是说我们不需要再去计算一个程序总共会起多少个task了。

    • 更容易获得更充分的资源利用。如果没有slot共享,那么非密集型操作source/flatmap就会占用同密集型操作 keyAggregation/sink 一样多的资源。如果有slot共享,将task的2个并行度增加到6个,能充分利用slot资源,同时保证每个TaskManager能平均分配到重的subtasks。

  • SlotSharingGroup[soft]

    SlotSharingGroup 是Flink中用来实现 slot 共享的类,它尽可能地让 subtasks 共享一个slot。

    保证同一个 group 的并行度相同的 sub-tasks 共享同一个slots。

    算子的默认group为default [即默认一个 Job下 的 subtask都可以共享一个 slot]

    为了防止不合理的共享,用户也能通过API来强制指定 operator 的共享组

    比如:someStream.filter(…).slotSharingGroup(“group1”);就强制指定了filter的slot共享组为group1。

    怎么确定一个未做SlotSharingGroup设置算子的SlotSharingGroup什么呢(根据上游算子的group 和自身是否设置group共同确定)。适当设置可以减少每个slot运行的线程数,从而整体上减少机器的负载。