Flink 2.3.0 从理论到实践 ------ 第 2 章 Flink 运行时架构
课程定位:本章深入 Flink 2.3.0 的内部运作机制,理解 JobManager / TaskManager 的协作、作业从提交到执行的完整链路、Task / SubTask / Slot 的资源模型,以及 Flink 2.x 引入的自适应调度与可插拔运行时。掌握架构是后续调优与排错的基础。
版本基线:Flink 2.3.0 + JDK 17
章节导读
- [2.1 架构总览:两层进程模型](#2.1 架构总览:两层进程模型)
- [2.2 核心组件详解](#2.2 核心组件详解)
- [2.3 作业执行链路:从提交到物理执行](#2.3 作业执行链路:从提交到物理执行)
- [2.4 任务与资源模型](#2.4 任务与资源模型)
- [2.5 执行模式:流 / 批 / 自动](#2.5 执行模式:流 / 批 / 自动)
- [2.6 高可用(HA)](#2.6 高可用(HA))
- [2.7 Flink 2.x 架构演进](#2.7 Flink 2.x 架构演进)
- [2.8 本章小结与下章预告](#2.8 本章小结与下章预告)
2.1 架构总览:两层进程模型
Flink 采用两层进程模型 :一个 JobManager 集群(控制面)+ 多个 TaskManager(数据面)。
┌──────────────────────────────────────────────────────────────────────┐
│ Flink 集群架构 │
└──────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────┐
│ JobManager(控制面) │
│ ┌───────────┐ ┌────────────┐ ┌───────────────┐ ┌───────────┐ │
│ │Dispatcher│ │JobMaster │ │ResourceManager│ │Web UI/REST│ │
│ │接收作业 │ │协调单个作业 │ │管理TM/资源 │ │监控入口 │ │
│ └───────────┘ └────────────┘ └───────────────┘ └───────────┘ │
└───────────────────────────────┬──────────────────────────────────┘
│ 心跳 + 任务调度
┌───────────────────────┼───────────────────────┐
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ TaskManager 1 │ │ TaskManager 2 │ │ TaskManager N │
│ ┌───────────┐ │ │ ┌───────────┐ │ │ ┌───────────┐ │
│ │ Slot 1 │ │ │ │ Slot 1 │ │ │ │ Slot 1 │ │
│ │ Slot 2 │ │ │ │ Slot 2 │ │ │ │ Slot 2 │ │
│ │ ... │ │ │ │ ... │ │ │ │ ... │ │
│ └───────────┘ │ │ └───────────┘ │ │ └───────────┘ │
│ 本地状态+网络 │ │ 本地状态+网络 │ │ 本地状态+网络 │
└───────────────┘ └───────────────┘ └───────────────┘
两个核心原则:
| 原则 | 说明 |
|---|---|
| JM 无状态(逻辑上),TM 有状态(物理上) | JM 负责调度与元数据,TM 承载数据计算与本地状态 |
| JM 与 TM 解耦 | JM 故障不影响正在运行的 TM 计算(可从 Checkpoint 恢复) |
2.2 核心组件详解
2.2.1 JobManager(JM)
JobManager 是 Flink 集群的控制面,负责作业的接收、调度、协调与监控。它内部包含以下组件:
| 组件 | 职责 |
|---|---|
| Dispatcher | 接收客户端提交的作业,启动 JobMaster,提供 REST API 与 Web UI |
| JobMaster | 每个作业一个 JobMaster,负责该作业的调度、Checkpoint 协调、故障恢复 |
| ResourceManager | 管理 TaskManager 的注册与资源分配,处理 Slot 请求与回收 |
| Web UI / REST | 提供作业监控、集群状态、历史查询等可视化入口 |
关键点 :JobMaster 是每作业一个,不是全局一个。多个作业共享同一个 JobManager 进程,但各自有独立的 JobMaster。
2.2.2 TaskManager(TM)
TaskManager 是 Flink 集群的数据面(Worker 进程),负责执行具体的计算任务。
| 组件 | 职责 |
|---|---|
| Task Slot | TM 的资源单位,一个 Slot 对应一组计算资源(CPU + 内存切片) |
| Task / SubTask | 实际运行在 Slot 中的计算线程 |
| Network Buffer | TM 间数据传输的缓冲区,基于 Netty |
| State Backend | 本地状态存储(HashMap / ForSt) |
Flink 2.x 变化:TM 的内存模型在 2.x 中进一步细化,引入了更精细的托管内存(Managed Memory)划分,用于状态、网络缓冲、批处理等。
2.2.3 客户端(Client)
客户端不是集群的一部分,负责:
-
将用户程序编译成 JobGraph
-
通过 Dispatcher 提交到集群
-
接收作业执行结果(可选)
用户代码 → Client 编译 → JobGraph → Dispatcher → JobMaster → 调度到 TM
2.3 作业执行链路:从提交到物理执行
一个 Flink 作业从用户提交到实际执行,要经历四层图结构的转化。
2.3.1 四层图结构
用户代码(StreamGraph)
│ Client 编译 + 优化
▼
JobGraph(可提交的作业图)
│ JobMaster 生成
▼
ExecutionGraph(并行执行图)
│ 调度器部署
▼
物理执行(Task 在 TM 的 Slot 中运行)
| 阶段 | 产物 | 生成者 | 说明 |
|---|---|---|---|
| 1. StreamGraph | 流图 | Client | 用户 API 调用生成的最初 DAG,算子间以边连接 |
| 2. JobGraph | 作业图 | Client 优化 | 算子链化(Operator Chain)+ 中间结果分区策略,可提交 |
| 3. ExecutionGraph | 执行图 | JobMaster | JobGraph 的并行化版本,每个算子展开为并行实例 |
| 4. 物理执行 | Task | 调度器 | ExecutionGraph 部署到 TM 的 Slot 中,实际运行 |
2.3.2 算子链化(Operator Chain)
为了减少线程切换与网络开销,Flink 会把能链在一起的算子 合并成一个 Task,在同一个线程中执行。
未链化(3 个 Task,3 次线程切换):
Source → Map → Filter → Sink
链化后(1 个 Task,0 次线程切换):
[Source → Map → Filter → Sink] ← 同一个线程
算子链化的条件(必须同时满足):
| 条件 | 说明 |
|---|---|
| 上下游并行度相同 | 并行度不同无法链化 |
| 上下游在同一个 Slot Sharing Group | 默认都在 default 组 |
| 数据转发策略为 Forward | 即 forward 分区,非 rebalance/keyBy |
| 没有禁用链化 | 未调用 disableChaining() |
| 上下游算子数量未超限 | 受 chain.length 限制 |
实践 :算子链化是 Flink 性能优化的重要手段。但在某些场景(如需要独立监控某个算子)下,可以通过
disableChaining()或slotSharingGroup()手动拆分。
2.3.3 调度过程
JobMaster 中的 Scheduler 负责将 ExecutionGraph 中的 Task 调度到 TM 的 Slot 中:
JobMaster(Scheduler)
│ 1. 请求 Slot
▼
ResourceManager
│ 2. 分配 TM + Slot
▼
TaskManager
│ 3. 启动 Task 线程
▼
Task 在 Slot 中运行
2.4 任务与资源模型
这是 Flink 中最容易混淆的概念。我们用一个具体例子来说明。
2.4.1 核心概念定义
| 概念 | 定义 |
|---|---|
| Operator(算子) | 用户定义的计算逻辑单元,如 map、keyBy、window |
| Task | 算子链化后的物理执行单元,一个 Task 包含一个或多个算子 |
| SubTask | Task 的并行实例,Task 有并行度 N 就有 N 个 SubTask |
| Slot | TaskManager 的资源单位,一个 Slot 可以运行多个 SubTask(不同 Task 的) |
| Parallelism(并行度) | 一个算子/Task 的并行实例数量 |
2.4.2 关系图解
假设一个作业有 3 个算子(Source、Map、Sink),并行度为 2,且能链化为一个 Task:
TaskManager(2 个 Slot)
┌────────────────────────────────────┐
│ Slot 0 │ Slot 1 │
│ ┌────────────┐ │ ┌────────────┐ │
│ │ SubTask 0 │ │ │ SubTask 1 │ │
│ │ [Source-0] │ │ │ [Source-1] │ │
│ │ [Map-0] │ │ │ [Map-1] │ │
│ │ [Sink-0] │ │ │ [Sink-1] │ │
│ └────────────┘ │ └────────────┘ │
└────────────────────────────────────┘
Task 数 = 1(链化后)
SubTask 数 = 2(并行度)
Slot 数 = 2(每个 SubTask 占 1 个 Slot)
2.4.3 Slot Sharing(槽共享)
Slot Sharing 是 Flink 的关键设计:同一个作业的不同 Task 的 SubTask 可以共享同一个 Slot。
假设 3 个 Task(A、B、C),并行度 2,2 个 Slot:
Slot 0 Slot 1
┌──────────────┐ ┌──────────────┐
│ A-SubTask-0 │ │ A-SubTask-1 │
│ B-SubTask-0 │ │ B-SubTask-1 │
│ C-SubTask-0 │ │ C-SubTask-1 │
└──────────────┘ └──────────────┘
3 个 Task × 2 并行度 = 6 个 SubTask
共享到 2 个 Slot 中(每个 Slot 跑 3 个 SubTask)
Slot Sharing 的好处:
| 好处 | 说明 |
|---|---|
| 资源利用率高 | 轻算子和重算子共享 Slot,避免轻算子占着 Slot 不干活 |
| Slot 数 = 最大并行度 | 只需保证 Slot 数 ≥ 作业的最大并行度即可 |
| 负载均衡 | 每个 Slot 运行完整的算子管线,负载更均匀 |
Slot Sharing Group:
- 默认所有算子在
default组,可共享 - 可通过
slotSharingGroup("name")为算子指定组,不同组之间不能共享 - 常用于隔离重型算子(如大状态的 Join)到独立 Slot
结论:作业所需的最少 Slot 数 = 所有 Slot Sharing Group 中的最大并行度。
2.4.4 并行度的来源与优先级
并行度的设置有四个来源,按优先级从高到低:
优先级 1: 算子级 .setParallelism(n) ← 最高
优先级 2: 环境级 env.setParallelism(n)
优先级 3: 客户端命令 -p n / flink run -p n
优先级 4: 集群默认 parallelism.default ← 最低
注意(来自实测经验) :Flink SQL 中
SET 'table.exec.default-parallelism' = 'n'设置的是 Table API 的默认并行度,它与 DataStream 的env.setParallelism()是两套机制。Kafka Source 的并行度由 Kafka 分区数决定,不受table.exec.default-parallelism控制。
2.4.5 资源配置示例
假设一个 TM 配置 taskmanager.numberOfTaskSlots: 4,taskmanager.memory.process.size: 8g:
| 配置项 | 值 | 说明 |
|---|---|---|
taskmanager.numberOfTaskSlots |
4 | 每个 TM 4 个 Slot |
taskmanager.memory.process.size |
8g | TM 总内存(含 JVM、托管、网络) |
taskmanager.memory.task.heap.size |
~3g | Task 堆内存 |
taskmanager.memory.managed.size |
~4g | 托管内存(状态、批处理) |
Slot 与内存的关系 :Slot 不独占内存,内存是 TM 级别共享的。Slot 主要是线程调度与资源隔离的逻辑单位,不是严格的内存隔离。
2.5 执行模式:流 / 批 / 自动
Flink 2.x 支持三种执行模式,通过 execution.runtime-mode 配置。
2.5.1 三种模式对比
| 模式 | execution.runtime-mode |
特点 | 适用场景 |
|---|---|---|---|
| 流模式 | streaming |
持续运行,处理无界流 | 实时计算 |
| 批模式 | batch |
处理有界数据,跑完即退出 | 离线 ETL |
| 自动模式 | automatic |
根据 Source 是否有界自动选择 | 流批一体 |
2.5.2 流模式 vs 批模式的执行差异
流模式(streaming):
Source ──► Map ──► KeyBy ──► Window ──► Sink
(持续运行, 数据逐条流动, 增量计算)
批模式(batch):
Source ──► Map ──► Shuffle ──► Reduce ──► Sink
(阶段执行, 上一阶段完成后才开始下一阶段, 全量计算)
| 维度 | 流模式 | 批模式 |
|---|---|---|
| 数据流动 | 逐条流式 | 按阶段(Stage)批量 |
| Shuffle 策略 | Pipeline(流水线) | Blocking(阻塞) |
| 状态 | 持续维护,Checkpoint 快照 | 阶段结束即释放 |
| 容错 | Checkpoint 恢复 | 重算整个阶段 |
| 调度 | 所有 Task 同时调度 | 按阶段依次调度 |
| 内存 | 状态 + 网络缓冲 | 排序、哈希表 |
2.5.3 自动模式
Source 有界? ──是──► batch 模式
│
否
▼
streaming 模式
适用 :同一套 SQL 既能跑实时又能跑离线时使用。例如
SELECT * FROM kafka_table(流)vsSELECT * FROM hive_table(批),设为automatic即可自动切换。
2.6 高可用(HA)
生产环境中,JobManager 的单点故障会导致整个集群不可用。Flink 通过 Standby JobManager 实现高可用。
2.6.1 HA 架构
┌────────────────────────────────────────────────────────────┐
│ ZooKeeper / Kubernetes │
│ (用于 Leader 选举 + 元数据持久化) │
└──────────────────┬─────────────────────┬───────────────────┘
│ │
┌────────▼────────┐ ┌────────▼────────┐
│ JobManager │ │ JobManager │
│ (Leader) │ │ (Standby) │
└─────────────────┘ └─────────────────┘
│
┌────────▼───────────────┐
│ TaskManager 集群 │
└─────────────────────────┘
2.6.2 HA 机制
| 机制 | 说明 |
|---|---|
| Leader 选举 | 多个 JM 竞争,选出一个 Leader,其余 Standby |
| 元数据持久化 | JobGraph、Checkpoint 元数据存到 ZooKeeper/K8s + 持久化存储 |
| 故障切换 | Leader 故障时,Standby 接管,从持久化存储恢复作业 |
2.6.3 两种 HA 方案
| 方案 | 依赖 | 适用场景 |
|---|---|---|
| ZooKeeper HA | ZooKeeper 集群 | YARN / Standalone 部署 |
| Kubernetes HA | K8s API Server | Kubernetes 部署 |
Flink 2.x 趋势:Kubernetes HA 方案逐渐成为主流,不依赖外部 ZooKeeper,运维更简单。
2.7 Flink 2.x 架构演进
Flink 2.0 对运行时架构做了重大重构,引入了两个关键能力。
2.7.1 Adaptive Scheduler(自适应调度器)
传统调度器在作业启动时静态分配 并行度与 Slot,运行中无法调整。Adaptive Scheduler 支持运行时动态调整:
| 维度 | 传统调度器 | Adaptive Scheduler |
|---|---|---|
| 并行度 | 启动时固定 | 可运行时增减 |
| Slot 分配 | 静态 | 动态申请/释放 |
| 扩缩容 | 需重启 | 在线调整 |
| 资源利用 | 按峰值规划 | 按需弹性 |
传统: 3 TM × 4 Slot = 12 Slot 全部预占
自适应: 闲时 2 TM × 2 Slot = 4 Slot,忙时自动扩到 12 Slot
2.7.2 Pluggable Runtime(可插拔运行时)
Flink 2.x 将运行时抽象为可插拔接口,允许接入不同的执行引擎:
Flink API / SQL 层
│
▼
┌─────────────────────────────────┐
│ Pluggable Runtime Interface │
├──────────────┬──────────────────┤
│ 传统执行引擎 │ 其他执行引擎 │
│ (Stream/Batch)│ (如 Flink-1.x │
│ │ 兼容模式等) │
└──────────────┴──────────────────┘
意义:为 Flink 未来支持多种执行后端(如自研引擎、异构计算)打下基础,是架构层的长期投资。
2.7.3 其他 2.x 架构变化
| 变化 | 说明 |
|---|---|
| ForSt 状态后端 | RocksDB 的替代实现,性能更优,2.x 默认推荐 |
| 内存模型细化 | 托管内存划分更精细,支持状态、网络、批处理独立配置 |
| 网络栈优化 | 基于 Netty 的网络栈升级,提升 TM 间吞吐 |
2.8 本章小结与下章预告
本章小结
┌────────────────────────────────────────────────────────────────┐
│ 第 2 章 要点回顾 │
└────────────────────────────────────────────────────────────────┘
✓ 两层进程模型:
JobManager(控制面) + TaskManager(数据面)
JM 内部: Dispatcher / JobMaster / ResourceManager / Web UI
✓ 作业四层图:
StreamGraph → JobGraph(算子链化) → ExecutionGraph(并行化) → 物理执行
算子链化条件: 同并行度 + 同 Slot Group + forward + 未禁用
✓ 资源模型:
Operator → Task(链化后) → SubTask(并行实例) → Slot(资源单位)
Slot Sharing: 同作业不同 Task 的 SubTask 可共享 Slot
最少 Slot 数 = 最大 Slot Sharing Group 的并行度
✓ 并行度优先级:
算子级 > 环境级 > 客户端命令 > 集群默认
Kafka Source 并行度 = Kafka 分区数
✓ 执行模式:
streaming(持续) / batch(阶段) / automatic(自动判断)
✓ 高可用:
ZooKeeper HA / Kubernetes HA
Leader 选举 + 元数据持久化 + Standby 接管
✓ Flink 2.x:
Adaptive Scheduler(在线扩缩容)
Pluggable Runtime(可插拔执行引擎)
ForSt 状态后端(替代 RocksDB)
下章预告
第 3 章 环境准备与集群部署 :动手搭建 Flink 2.3.0 运行环境。涵盖 JDK 17 与依赖准备、本地模式安装、Standalone 集群、YARN 三种模式(Session / Per-Job / Application)、Kubernetes Operator 部署、
flink-conf.yaml核心配置参数、Web UI 与 REST API 基础使用。
官方参考资料
- Flink 架构官方文档:https://nightlies.apache.org/flink/flink-docs-stable/docs/concepts/flink-architecture/
- Task Slot 与资源:https://nightlies.apache.org/flink/flink-docs-stable/docs/concepts/flink-architecture/#task-slots-and-resources
- 执行模式:https://nightlies.apache.org/flink/flink-docs-stable/docs/dev/datastream/execution_mode/
- 高可用配置:https://nightlies.apache.org/flink/flink-docs-stable/docs/deployment/ha/overview/