续上篇《Flink 的编程模型》,这篇主要关于Flink 的分布式执行环境。
Tasks and Operator Chains (任务和算子链)
在 Flink 分布式执行环境中,会将多个运算子任务 Operator Subtask 串起来组成一个Operator Chain,实际上就是一个算子链。每个算子会在 TaskManager 上在一个独立的线程中执行。将算子串连到任务中是一种很好的优化:它能减少线程间的数据交接和缓存,并且提高整体的吞吐,降低处理的时延。这种串联的操作,可以通过 API 来进行配置。
如下图的数据流就有5个子任务,通过5个并行的线程来执行,所示:
Job Managers,Task Managers,Clients
Flink的运行时,由两种类型的进程组成:
- JobManagers: 也就是 masters ,协调分布式任务的执行 。用来调度任务,协调checkpoints,协调错误恢复等等。至少需要一个 JobManager,高可用的系统会有多个,一个leader,其他是standby;
- TaskManagers: 也就是 workers,用来执行数据流任务或者子任务,缓存和交互数据流。 至少需要一个TaskManager
但 Client 不是运行是和程序执行的一部分,它是用来准备和提交数据流到 JobManagers,之后,可以断开连接或者保持连接以获取任务的状态信息。
从上图可以分析出 Flink 运行时的整体状态。
- Flink 的 Driver 程序会将代码逻辑构建成一个Program Dataflow (区分source,operator,sink等等),在通过 Graph Builder 构建 DAG 的Dataflow graph, 构建 job,划分出 task 和 subtask 等等。
- Client 将 job 提交到 JobManager。 Client 通过 Actor System 和 JobManager 进行消息通讯,接收 JobManager返回的状态更新和任务执行统计结果。
- JobMangaer 按照 Dataflow 的 Task 和 Subtask 的划分,将任务调度分配到各个 TaskManager 中进行执行。
- TaskManager 会将内存抽象成多个TaskSlot,用于执行 Task 任务。JobManagers 与 TaskManagers 之间的任务管理,Checkpoints 的触发,任务状态,心跳等等消息处理都是通过 ActorSystem。
Task Slots and Resources (任务槽和资源)
每个 Worker (Task Manager) 是一个 JVM 进程,通常会在单独的线程里执行一个或者多个子任务。为了控制一个 Worker 能够接受多少个任务,会在 Worker 上抽象多个 Task Slot (至少一个)。
每个 Task Slot 代表固定的资源子集。比如一个 TaskManager 有3个Slots,每个Slot能管理对这个 Worker 分配的资源的 3分之1 的内存。 对资源分槽,意味着 Subtask 不会同其他Subtasks 竞争内存,同时可以预留一定的可用内存。目前 Task Slot 没有对CPU进行隔离,仅是针对内存。通过动态的调整 task slots 的个数,用户可以定义哪些子任务可以相互隔离。只有一个 slot 的 TaskManager 意味着每个任务组运行在一个单独 JVM 中。 在拥有多个 slot 的 TaskManager 上,subtask 共用 JVM,可以共用 TCP 连接和心跳消息,同时可以共用一些数据集和数据结构,从而减小任务的开销。
默认情况下,Flink允许子任务共享 slots,即便它们是不同任务的子任务,只要属于同一个job。这样的结果就是一个 slot 会负责一个 job 的整个 pipeline。共用 slot 有两个好处:
- Flink 集群的 task slot 的个数就是 job 的最高并行度。
- 更实现更好的资源利用。没有共享的slots,非密集的
source/map()
操作, subtask 会占用和 window 这类密集型的 subtask 同样多的资源。 使用共享的 slot 的将充分的利用分槽的资源,使代价较大的 subtask 能够均匀的分布在 TaskManager 上。
如下图中的共享 slot 的执行模式中可以并行运行 6 个 pipeline 而上图的只可以运行 2 个pipeline。同时 APIs 也提供了资源组的机制,可以实现不想进行资源隔离的情况。
根据经验,一个很好的默认任务槽数就是 CPU 核心数。使用超线程,每个插槽然后需要2个或更多硬件线程上下文。
State Backends (状态后端)
数据的 KV 索引信息存储在设定的状态后端的存储中。一种是内存中的 Hash map,另一种是存在 Rocksdb(KV存储)中。另外,状态后端实现了在时间点上对 KV 状态的快照,并作为 Checkpoint 的一部分存储起来。
Savepoints (保存点)
用 Data Stream API 编写的程序可以从保存点恢复执行。保存点允许更新程序和Flink群集,而不会丢失任何状态。
保存点是手动触发的检查点,它会获取程序的 SNAPSHOT 并将其写入状态后台。他们依靠常规的检查点机制。在执行期间,程序会定期在工作节点上创建 SNAPSHOT 并生成检查点。对于恢复,仅需要最后完成的检查点,并且一旦新的检查点完成,就可以安全地丢弃旧的检查点。
Savepoints 和周期性的 Checkpoint 非常的类似,只是有两个重要的不同。一个是由用户触发,而且不会随着新的 Checkpoint 生成而被丢弃,但可以从命令行或通过 REST API 取消作业时创建保存点。
👊 结束。
本文由 Chakhsu Lau 创作,采用 知识共享署名4.0 国际许可协议进行许可。
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。