当前位置: 首页 > news >正文

2.4 Flink运行时架构:Task、SubTask、ExecutionGraph的关系

在理解Flink运行时架构之前,我们先用一个生活化的比喻来建立直观认识:

想象你是一家大型工厂的总经理,需要生产一批复杂的产品。你会怎么做?

  1. 制定生产计划:首先画出生产流程图,明确每个环节的工作内容
  2. 分解任务:将复杂的生产过程分解为多个可并行的工序
  3. 分配工人:为每个工序安排合适数量的工人并行作业
  4. 协调执行:确保各个工序之间的协调配合

Flink的运行时架构正是这样一个"智能工厂"的管理系统。

ExecutionGraph:生产总指挥图

什么是ExecutionGraph?

ExecutionGraph就像是工厂的总生产指挥图,它是Flink程序在运行时的完整执行计划。

// 用户编写的Flink程序(简化示例)
DataStream<String> source = env.addSource(new FlinkKafkaConsumer<>(...));
DataStream<WordCount> counts = source.flatMap(new Tokenizer())           // 分词算子.keyBy(value -> value.word)         // 按key分组.window(TumblingEventTimeWindows.of(Time.seconds(10)))  // 窗口.sum("count");                      // 聚合算子
counts.addSink(new FlinkKafkaProducer<>(...));

这段用户代码经过Flink内部转换,最终形成ExecutionGraph:

JobGraph (逻辑计划)↓
ExecutionGraph (物理执行计划)↓
实际运行的Task和SubTask

ExecutionGraph的关键特征

  1. 包含并行度信息:每个算子应该启动多少个并行实例
  2. 包含资源分配:每个并行实例需要多少资源
  3. 包含数据流向:数据如何在各个并行实例之间流转
  4. 包含容错信息:如何进行checkpoint和故障恢复

Task:工厂中的生产线

Task的概念

Task可以理解为工厂中的一条完整生产线。由于算子链(Operator Chain)的优化,多个相邻的算子会被合并到同一个Task中执行。

// 原始算子链
Source -> FlatMap -> Map -> KeyBy -> Window -> Sum -> Sink// 经过算子链优化后,可能形成这样的Task:
Task1: Source -> FlatMap -> Map  (算子链合并)
Task2: KeyBy -> Window -> Sum    (算子链合并)  
Task3: Sink

为什么要有算子链?

就像工厂为了提高效率,会把相关的工序安排在同一条生产线上,避免半成品在不同车间之间频繁搬运。

算子链的好处:

  • 减少数据序列化/反序列化开销
  • 减少网络传输
  • 减少线程切换
  • 提高整体处理效率

Task的实际示例

public class ChainedMapTask extends StreamTask<String, StreamMap<String, String>> {@Overrideprotected void init() {// 初始化算子链中的所有算子SourceFunction sourceOperator = ...;MapFunction mapOperator = ...;// 构建算子链}@Overrideprotected void processInput() {// 处理输入数据,在算子链中依次执行while (isRunning()) {Record record = sourceOperator.next();Record mapped = mapOperator.map(record);output.collect(mapped);}}
}

SubTask:生产线上的具体工位

SubTask的概念

如果Task是一条生产线,那么SubTask就是这条生产线上的具体工位。当我们设置并行度为4时,一个Task会被分解为4个SubTask,就像一条生产线复制了4份,同时工作。

// 设置并行度
source.flatMap(new Tokenizer()).setParallelism(4);  // 创建4个SubTask// 在TaskManager中的实际执行
SubTask-0: 处理数据分区0
SubTask-1: 处理数据分区1  
SubTask-2: 处理数据分区2
SubTask-3: 处理数据分区3

SubTask的生命周期

public class SubTask {// 1. 初始化阶段public void initialize() {setupOperators();initializeState();registerMetrics();}// 2. 运行阶段  public void run() {while (isRunning()) {processNextRecord();if (shouldCheckpoint()) {performCheckpoint();}}}// 3. 清理阶段public void cleanup() {closeOperators();releaseResources();}
}

SubTask之间的数据交换

SubTask之间通过数据分区网络传输进行协作:

// KeyBy操作会触发数据重分布
stream.keyBy(record -> record.getUserId())  // 按用户ID分区.map(new UserProcessor());// 数据流转示意:
SubTask-0: 用户1,5,9...  →  重分区  →  SubTask-0: 所有用户1的数据
SubTask-1: 用户2,6,10... →  重分区  →  SubTask-1: 所有用户2的数据  
SubTask-2: 用户3,7,11... →  重分区  →  SubTask-2: 所有用户3的数据
SubTask-3: 用户4,8,12... →  重分区  →  SubTask-3: 所有用户4的数据

三者关系总结

让我们用一个完整的示例来理解三者关系:

// 1. 用户程序
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();DataStream<String> lines = env.socketTextStream("localhost", 9999);
DataStream<Tuple2<String, Integer>> counts = lines.flatMap(new Tokenizer())                    // 并行度4.keyBy(value -> value.f0).timeWindow(Time.seconds(5)).sum(1);                                     // 并行度4counts.print();                                  // 并行度1

转换过程:

1. ExecutionGraph层面:

ExecutionVertex-1: Source+FlatMap (并行度4)
ExecutionVertex-2: KeyBy+Window+Sum (并行度4)  
ExecutionVertex-3: Print (并行度1)

2. Task层面:

Task-1: [Source -> FlatMap] 算子链
Task-2: [KeyBy -> Window -> Sum] 算子链
Task-3: [Print]

3. SubTask层面:

Task-1的SubTask实例:- SubTask-1-0 (处理数据分片0)- SubTask-1-1 (处理数据分片1)  - SubTask-1-2 (处理数据分片2)- SubTask-1-3 (处理数据分片3)Task-2的SubTask实例:- SubTask-2-0 (处理特定key的数据)- SubTask-2-1 (处理特定key的数据)- SubTask-2-2 (处理特定key的数据)  - SubTask-2-3 (处理特定key的数据)Task-3的SubTask实例:- SubTask-3-0 (汇总所有结果)

性能调优要点

理解了这三者关系后,我们就能更好地进行性能调优:

1. 合理设置并行度

// 根据数据量和CPU核数设置
env.setParallelism(Runtime.getRuntime().availableProcessors());// 为不同算子设置不同并行度
source.setParallelism(2);     // IO密集型,并行度可以适当小些
transform.setParallelism(8);  // 计算密集型,并行度可以大些
sink.setParallelism(1);       // 输出汇总,通常并行度为1

2. 优化算子链

// 禁用算子链(在需要时)
someStream.map(new MyMapper()).disableChaining()      // 禁用与下游算子的链接.keyBy(...).startNewChain()        // 从这里开始新的算子链.sum(1);

3. 监控SubTask运行状况

// 通过Flink Web UI观察:
// - 各个SubTask的吞吐量是否均衡
// - 是否存在数据倾斜
// - 网络传输是否成为瓶颈
// - SubTask的CPU和内存使用情况

小结

  • ExecutionGraph:整个作业的执行蓝图,包含所有执行细节
  • Task:经过算子链优化的执行单元,是逻辑上的"工作组"
  • SubTask:Task的并行实例,是实际执行计算的"工人"

三者关系就像建筑施工:ExecutionGraph是施工总图纸,Task是各个专业工种组(如水电组、瓦工组),SubTask是每个工种组里的具体工人。理解这个关系有助于我们更好地设计和优化Flink应用程序。

http://www.dtcms.com/a/350324.html

相关文章:

  • OPcache 高级技术文档:原理、监控与优化实践
  • Unity使用Sprite切割大图
  • JavaScript 性能优化实战:从理论到落地的技术文章大纲
  • 基于长短期记忆网络的多变量时间序列预测 LSTM
  • Redis 哨兵 Sentinel
  • 【沉浸式解决问题】NVIDIA 显示设置不可用。 您当前未使用连接到NVIDIA GPU 的显示器。
  • 实时监测蒸汽疏水阀的工作状态的物联网实时监控平台技术解析
  • VLLM的加速原理
  • 基于MATLAB实现支持向量机(SVM)进行预测备
  • 大模型的多机多卡训练
  • 神经网络|(十五)概率论基础知识-协方差标准化和皮尔逊相关系数
  • 亚马逊AWD美西新仓上线:旺季备货的效率革命与策略升级
  • 真实应急响应案例记录
  • 机器学习笔记
  • Neumann Networks for Linear Inverse Problems in Imaging论文阅读
  • CF2133D 鸡骑士
  • 基于遗传算法优化BP神经网络的时间序列预测 GA-BP
  • PNP机器人介绍:全球知名具身智能/AI机器人实验室介绍之多伦多大学机器人研究所
  • DeepSeek 14B模型本地部署与预训练实现方案
  • jsvmp是什么,如何使用
  • 入门Ubuntu操作系统
  • 深度学习:从手写数字识别案例认识pytorch框架
  • 用 GSAP + ScrollTrigger 打造沉浸式视频滚动动画
  • 《零基础学 C 语言文件顺序读写:fputc/fgetc 到 fread/fwrite 函数详解》
  • 并行算法与向量化指令集的实战经验
  • 【Linux内核实时】实时互斥锁 - sched_rt_mutex
  • 寂静之歌 单机+联机(Songs Of Silence)免安装中文版
  • 数据存储的思考——从RocketMQ和Mysql的架构入手
  • 力扣498 对角线遍历
  • Qwen2-Plus与DeepSeek-V3深度测评:从API成本到场景适配的全面解析