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(流)vs SELECT * 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,运维更简单。


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 基础使用。

官方参考资料

相关推荐
xiaoye-duck1 小时前
《Linux 网络编程》从 0 手写 Reactor 反应堆(上):拆解 Reactor 架构 —— 连接抽象、Poller 封装与事件派发核心
linux·网络
砚凝霜1 小时前
【软考信息安全】第十二章 网络安全审计概述与分类
网络·安全·web安全
酣大智1 小时前
DHCP Option 选项
网络·dhcp
俊哥大数据1 小时前
Flink 2.3.0 从理论到实践 —— 第 4 章 状态管理(State)
flink
迪康妍妍1 小时前
终端安全实战:用迪康终端安全管理系统实现U盘四分档管控与全量审计
android·运维·网络·安全·电脑
俊哥大数据1 小时前
Flink 2.3.0 从理论到实践 —— 第 5 章 时间语义与 Watermark
flink
一只旭宝1 小时前
【网络精讲1】TCP 三次握手:为什么是三次,第三次丢了怎么办
服务器·网络·tcp/ip
JAVA面经实录9171 小时前
Java高级后端 · 架构组件开发 + CodeReview(本岗位差异化核心)
java·架构·代码复审
代码山河1 小时前
Java语言特点:跨平台、面向对象、安全性详解
java·学习·架构·教程·面向对象·项目