Flink 底层原理系列(二):运行时架构
2021-01-12·5 分钟阅读
前言
理解 Flink 的运行时架构对于优化作业性能和排查问题至关重要。本章将深入解析 Task Slot、Slot Sharing、作业调度以及执行图的转换过程。
技术亮点
| 技术点 | 难度 | 面试价值 | 本文覆盖 |
|---|---|---|---|
| Task Slot | ⭐⭐⭐ | 高频考点 | ✅ |
| Slot Sharing | ⭐⭐⭐⭐ | 高频考点 | ✅ |
| 执行图层级 | ⭐⭐⭐⭐ | 进阶考点 | ✅ |
| 作业调度 | ⭐⭐⭐ | 实战价值 | ✅ |
面试考点
- Task Slot 是什么?为什么需要 Slot?
- 什么是 Slot Sharing?有什么好处?
- Flink 的执行图有哪几个层级?
- Flink 如何调度任务?
Task Slot 详解
Slot 概念
┌─────────────────────────────────────────────────────────────────────────┐
│ Task Slot 概念 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Task Slot 是 Flink 中资源隔离的基本单位: │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ TaskManager (12GB 内存, 4 CPU, 4 Slots) │ │
│ │ ┌───────────────────────────────────────────────────────────┐ │ │
│ │ │ │ │ │
│ │ │ Slot 1 (3GB) Slot 2 (3GB) Slot 3 (3GB) Slot 4 (3GB)│ │
│ │ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐│ │ │
│ │ │ │ Task │ │ Task │ │ Task │ │ Task ││ │ │
│ │ │ │ Chain │ │ Chain │ │ Chain │ │ Chain ││ │ │
│ │ │ │ A+B+C │ │ D+E+F │ │ G+H+I │ │ J+K+L ││ │ │
│ │ │ └───────────┘ └───────────┘ └───────────┘ └───────────┘│ │ │
│ │ │ │ │ │
│ │ └───────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Slot 的特点: │
│ • 资源隔离:每个 Slot 拥有独立的内存资源 │
│ • CPU 共享:同一 TaskManager 的 Slot 共享 CPU │
│ • 内存分配:TaskManager 内存按 Slot 数量均分 │
│ • 独立任务:一个 Slot 可以执行一个完整的 Operator Chain │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Slot 的作用
┌─────────────────────────────────────────────────────────────────────────┐
│ Slot 的作用 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. 资源隔离 │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ • 不同作业/任务之间内存隔离 │ │
│ │ • 防止一个任务耗尽整个 TaskManager 内存 │ │
│ │ • 提供稳定的资源保证 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ 2. 并行度控制 │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 作业并行度 = 该作业占用的 Slot 数量 │ │
│ │ │ │
│ │ 示例:并行度 4 的作业需要 4 个 Slot │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ 3. 资源利用优化 │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ • 多个 Operator 可以共享同一个 Slot │ │
│ │ • 减少 JVM 进程数量 │ │
│ │ • 减少 TCP 连接和网络开销 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Slot Sharing
Slot Sharing 原理
┌─────────────────────────────────────────────────────────────────────────┐
│ Slot Sharing 原理 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 无 Slot Sharing(每个 Operator 独占 Slot): │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Source(2) ──► Map(2) ──► Filter(2) ──► Sink(2) │ │
│ │ │ │ │ │ │ │
│ │ ▼ ▼ ▼ ▼ │ │
│ │ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │ │
│ │ │Slot 1│ │Slot 3│ │Slot 5│ │Slot 7│ │ │
│ │ │Slot 2│ │Slot 4│ │Slot 6│ │Slot 8│ │ │
│ │ └──────┘ └──────┘ └──────┘ └──────┘ │ │
│ │ │ │
│ │ 需要 8 个 Slot!资源利用率低 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ Slot Sharing │
│ 有 Slot Sharing(Operator Chain 共享 Slot): │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Source(2) ──► Map(2) ──► Filter(2) ──► Sink(2) │ │
│ │ │ │ │ │ │ │
│ │ └───────────┴───────────┴───────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌────────────────┐ │ │
│ │ │ Slot 1: S+M+F+Si│ │ │
│ │ │ Slot 2: S+M+F+Si│ │ │
│ │ └────────────────┘ │ │
│ │ │ │
│ │ 只需要 2 个 Slot!资源利用率高 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Slot Sharing 的好处: │
│ • 减少资源占用 │
│ • 减少网络通信(Operator Chain 内部本地转发) │
│ • 提高资源利用率 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Operator Chain
┌─────────────────────────────────────────────────────────────────────────┐
│ Operator Chain │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Operator Chain 是将多个 Operator 合并为一个 Task执行单元: │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Chain 条件: │ │
│ │ ┌───────────────────────────────────────────────────────────┐ │ │
│ │ │ │ │ │
│ │ │ 1. Forward 连接(一对一,不重分区) │ │ │
│ │ │ 2. 并行度相同 │ │ │
│ │ │ 3. 没有禁用 Chain(startNewChain/disableChain) │ │ │
│ │ │ │ │ │
│ │ └───────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ Chain 示例: │ │
│ │ ┌───────────────────────────────────────────────────────────┐ │ │
│ │ │ │ │ │
│ │ │ [Source] ──► [Map] ──► [Filter] ──► [KeyBy] ──► [Sink] │ │ │
│ │ │ │ │ │ │ │ │ │ │
│ │ │ └──────────┴──────────┘ └───────────┘ │ │ │
│ │ │ │ │ │ │ │
│ │ │ ▼ ▼ │ │ │
│ │ │ Chain 1 Chain 2 │ │ │
│ │ │ (Source+Map+Filter) (KeyBy+Sink) │ │ │
│ │ │ │ │ │
│ │ │ KeyBy 导致数据重分区,因此不能与前一个 Chain 合并 │ │ │
│ │ │ │ │ │
│ │ └───────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Chain 好处: │
│ • 减少线程切换开销 │
│ • 减少序列化/反序列化 │
│ • 减少网络传输 │
│ • 提高性能 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
执行图层级
四层执行图
┌─────────────────────────────────────────────────────────────────────────┐
│ 四层执行图转换 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 1. StreamGraph(用户逻辑图) │ │
│ │ ┌───────────────────────────────────────────────────────────┐ │ │
│ │ │ │ │ │
│ │ │ • 用户通过 DataStream API 构建的逻辑图 │ │ │
│ │ │ • 每个 Operator 对应一个 StreamNode │ │ │
│ │ │ • 包含转换逻辑和用户函数 │ │ │
│ │ │ │ │ │
│ │ └───────────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ StreamGraph -> JobGraph │ │
│ │ 2. JobGraph(作业图) │ │
│ │ ┌───────────────────────────────────────────────────────────┐ │ │
│ │ │ │ │ │
│ │ │ • Operator Chain 合并为一个 JobVertex │ │ │
│ │ │ • 包含序列化后的用户函数 │ │ │
│ │ │ • JobVertex 之间通过 IntermediateDataSet 连接 │ │ │
│ │ │ • 是提交给 JobManager 的数据结构 │ │ │
│ │ │ │ │ │
│ │ └───────────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ JobGraph -> ExecutionGraph │ │
│ │ 3. ExecutionGraph(执行图) │ │
│ │ ┌───────────────────────────────────────────────────────────┐ │ │
│ │ │ │ │ │
│ │ │ • JobVertex 展开为 ExecutionJobVertex │ │ │
│ │ │ • 每个并行度对应一个 ExecutionVertex │ │ │
│ │ │ • 包含状态管理和调度信息 │ │ │
│ │ │ • 是 JobMaster 管理的核心数据结构 │ │ │
│ │ │ │ │ │
│ │ └───────────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ ExecutionGraph -> Physical Graph │ │
│ │ 4. Physical Graph(物理执行图) │ │
│ │ ┌───────────────────────────────────────────────────────────┐ │ │
│ │ │ │ │ │
│ │ │ • ExecutionVertex 映射到具体的 TaskManager │ │ │
│ │ │ • 包含实际的部署信息 │ │ │
│ │ │ • TaskManager 执行的基本单位 │ │ │
│ │ │ │ │ │
│ │ └───────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
图转换示例
┌─────────────────────────────────────────────────────────────────────────┐
│ 图转换示例 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 用户代码: │
│ env.addSource(new MySource()) │
│ .map(new MyMap()) │
│ .keyBy(x -> x.key) │
│ .sum("value") │
│ .addSink(new MySink()); │
│ │
│ StreamGraph: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ [Source] ──► [Map] ──► [KeyBy] ──► [Sum] ──► [Sink] │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ JobGraph(并行度=2,Chain 优化后): │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ JobVertex 1 JobVertex 2 │ │
│ │ ┌──────────────────┐ ┌──────────────────┐ │ │
│ │ │ Source + Map │ │ KeyBy + Sum + Sink│ │ │
│ │ │ (parallelism=2) │─────────►│ (parallelism=2) │ │ │
│ │ └──────────────────┘ └──────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ExecutionGraph: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ ExecutionJobVertex 1 ExecutionJobVertex 2 │ │
│ │ ┌──────────────────┐ ┌──────────────────┐ │ │
│ │ │ ExecutionVertex 0│ │ ExecutionVertex 0│ │ │
│ │ │ ExecutionVertex 1│─────────►│ ExecutionVertex 1│ │ │
│ │ └──────────────────┘ └──────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
作业调度
调度策略
┌─────────────────────────────────────────────────────────────────────────┐
│ 作业调度策略 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Flink 支持两种调度模式: │
│ │
│ 1. Eager 调度(立即调度) │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ • 流作业默认模式 │ │
│ │ • 所有任务同时启动 │ │
│ │ • 需要足够的资源 │ │
│ │ │ │
│ │ ┌───────────────────────────────────────────────────────────┐ │ │
│ │ │ │ │ │
│ │ │ 任务 A ──► 任务 B ──► 任务 C │ │ │
│ │ │ │ │ │ │ │ │
│ │ │ ▼ ▼ ▼ │ │ │
│ │ │ 全部同时启动 │ │ │
│ │ │ │ │ │
│ │ └───────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ 2. Lazy 调度(延迟调度) │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ • 批作业默认模式 │ │
│ │ • 上游有数据时才启动下游 │ │
│ │ • 节省资源 │ │
│ │ │ │
│ │ ┌───────────────────────────────────────────────────────────┐ │ │
│ │ │ │ │ │
│ │ │ 任务 A ──► 任务 B ──► 任务 C │ │ │
│ │ │ │ │ │ │
│ │ │ ▼ │ │ │
│ │ │ 先启动 A │ │ │
│ │ │ │ │ │ │
│ │ │ ▼ A 完成后 │ │ │
│ │ │ 启动 B │ │ │
│ │ │ │ │ │ │
│ │ │ ▼ B 完成后 │ │ │
│ │ │ 启动 C │ │ │
│ │ │ │ │ │
│ │ └───────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
任务状态
┌─────────────────────────────────────────────────────────────────────────┐
│ 任务状态流转 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ ┌──────────────┐ │ │
│ │ │ CREATED │ │ │
│ │ └──────┬───────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌──────────────┐ │ │
│ │ │ SCHEDULED │ │ │
│ │ └──────┬───────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌──────────────┐ │ │
│ │ │ DEPLOYING │ │ │
│ │ └──────┬───────┘ │ │
│ │ │ │ │
│ │ ┌────────┴────────┐ │ │
│ │ ▼ ▼ │ │
│ │ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ RUNNING │ │ FAILED │ │ │
│ │ └──────┬───────┘ └──────────────┘ │ │
│ │ │ │ │
│ │ ├──────────────────────┐ │ │
│ │ ▼ ▼ │ │
│ │ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ FINISHED │ │ CANCELING │ │ │
│ │ └──────────────┘ └──────┬───────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌──────────────┐ │ │
│ │ │ CANCELED │ │ │
│ │ └──────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ 状态说明: │
│ • CREATED:任务创建 │
│ • SCHEDULED:任务已调度,等待资源 │
│ • DEPLOYING:任务部署中 │
│ • RUNNING:任务运行中 │
│ • FINISHED:任务正常结束 │
│ • CANCELING/CANCELED:任务取消中/已取消 │
│ • FAILED:任务失败 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
总结
本章深入解析了 Flink 运行时架构:
| 概念 | 说明 |
|---|---|
| Task Slot | 资源隔离的基本单位,内存独立,CPU 共享 |
| Slot Sharing | 多个 Operator Chain 共享 Slot,提高资源利用率 |
| Operator Chain | 合并多个 Operator 为一个 Task,减少开销 |
| 执行图 | StreamGraph → JobGraph → ExecutionGraph → Physical Graph |
参考资料
下一章预告
下一章将深入解析 数据流模型,包括:
- DataStream API 设计
- Transformation 与 Operator
- 数据交换模式