Flink运行架构及并行度设置
实时大数据处理的本质,是利用有限的资源实现高并发、低延迟的任务执行。Flink作为流批一体的分布式计算引擎,其运行架构和并行度机制是理解Flink性能和资源利用率的核心。本文将通过原理讲解、源码细节、实用口诀和案例演示,帮助你全面掌握Flink的运行机制与并行度设置。
一、Flink运行时架构
Flink的运行时架构由三大核心组件组成:JobManager、TaskManager、Slot。
1. JobManager(作业管理器)
- 职责:负责作业的调度、资源分配、故障恢复、Checkpoint管理等。
- 生命周期:每个Flink集群至少有一个JobManager(高可用场景下有主备)。
- 源码入口:
org.apache.flink.runtime.jobmanager.JobManagerRunner
2. TaskManager(任务管理器)
- 职责:负责实际的数据处理和计算任务执行。每个TaskManager进程可同时运行多个任务。
- Slot:TaskManager内部的资源切分单元。每个Slot可运行一个子任务(SubTask)。
- 源码入口:
org.apache.flink.runtime.taskexecutor.TaskExecutor
3. Slot(槽位)
- 定义:Slot是TaskManager对外暴露的“资源租赁单元”,决定了并发运行的最大任务数。
- 分配原则:一个SubTask占用一个Slot,多个Slot可以分配在不同TaskManager或同一个TaskManager上。
架构示意图
+-----------------+ +---------------------+
| JobManager |<----->| TaskManager N |--- Slot N.1
+-----------------+ |---------------------|--- Slot N.2| +---------------------+|| +---------------------++---------------> | TaskManager M |--- Slot M.1|---------------------|--- Slot M.2+---------------------+
口诀速记:
Job管调度,Task管执行,Slot管资源,SubTask落其上。
二、Flink作业提交流程
- 客户端提交作业
用户通过CLI/API提交作业,生成JobGraph(作业逻辑DAG)。 - JobManager接收作业
JobManager将JobGraph优化成ExecutionGraph(物理执行计划),决定并行度、资源分配等。 - 资源申请与Slot分配
JobManager向ResourceManager申请Slot资源,分配给各个SubTask。 - TaskManager启动SubTask
TaskManager收到任务后,启动对应的SubTask(线程),并进行数据处理。 - 状态管理与容错
Checkpoint机制保证作业可恢复,JobManager监控任务健康。
源码解释:
- 提交作业入口:
org.apache.flink.client.program.ClusterClient.submitJob
- 作业调度分配:
org.apache.flink.runtime.scheduler.SchedulerBase
- Slot分配核心:
org.apache.flink.runtime.jobmaster.slotpool.SlotPoolImpl
口诀速记:
客户端提交,JobManager调度,ResourceManager分配,TaskManager执行。
三、并行度设置
Flink的并行度决定了作业的吞吐能力和资源利用率。合理设置并行度是性能调优的关键。
1. 全局并行度(Global Parallelism)
- 定义:作业中所有算子的默认并行度。
- 设置方式:
- 配置文件(
flink-conf.yaml
):parallelism.default
- 代码设置:
env.setParallelism(4);
- 命令行参数:
-p 4
- 配置文件(
- 作用范围:所有未单独设置并行度的算子。
2. 算子并行度(Operator Parallelism)
- 定义:单个算子的并行度,可覆盖全局并行度。
- 设置方式:
dataStream.map(...).setParallelism(2);
- 适用场景:特定算子(如sink、source)性能或资源瓶颈时单独调优。
3. Slot资源分配与并行度关系
-
规则:
- 每个SubTask占用一个Slot。
- Flink支持“Slot Sharing”,同一个Slot内可运行不同算子的子任务(前提是属于同一个作业)。
-
Slot分配公式:
作业最大并行度 ≤ 集群总Slot数 集群总Slot数 = TaskManager数量 × 每个TaskManager Slot数
-
最佳实践:
- TaskManager的Slot数建议等于CPU核数。
- 并行度不宜超过总Slot数,否则任务会排队等待资源。
- Slot分配不均时,合理调整TaskManager数量或Slot数。
源码解释:
- 并行度决定在
JobGraph
到ExecutionGraph
转换时:org.apache.flink.runtime.jobgraph.JobVertex#setParallelism
- Slot分配在
SlotPoolImpl#allocateSlot
实现。
口诀速记:
全局定默认,算子可覆盖,Slot定上限,核数做参考。
四、案例演示
1. 环境准备
- 假设有3台机器,每台部署1个TaskManager,每个TaskManager分配4个Slot。
- 集群总Slot数:3 × 4 = 12
2. 代码案例
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();// 设置全局并行度为4
env.setParallelism(4);DataStream<String> source = env.addSource(new FlinkKafkaConsumer<>(...)); // Source未指定并行度,默认4DataStream<String> mapped = source.map(value -> value.toUpperCase()) // Map未指定并行度,默认4.setParallelism(8); // Map算子单独设置并行度为8mapped.addSink(new MySinkFunction()) // Sink未设置并行度,继承全局并行度4.name("MySink");env.execute("Parallelism Demo");
3. 并行度与Slot分配分析
- Source:并行度4(占用4个Slot)
- Map:并行度8(占用8个Slot)
- Sink:并行度4(占用4个Slot)
此作业最大并行度为8,因此至少需要8个Slot可用。由于集群有12个Slot,资源充足。
4. 如何查看实际并行度与Slot分配?
- 提交作业后,Flink Web UI(默认8081端口)可以查看各算子的并行度和Slot分配情况。
- 日志中也可看到Slot分配和任务调度日志。
5. 并行度设置口诀
并行度看Slot,Slot看核数,默认可全局,特殊算子调。
五、Slot Sharing机制及Chaining机制
1. Slot Sharing机制
定义:
Flink支持多个算子的SubTask共享同一个Slot资源(只要属于同一个作业且可Chaining),以提升资源利用率,降低资源碎片。
原理细节:
- Slot不是进程或线程,而是任务分组的“容器”。
- 同一作业内的多个SubTask可以被“装入”同一个Slot,前提是这些算子之间可以Chaining。
- 这样即使作业中有多个并行的算子,也不必为每个SubTask单独分配Slot,提升了资源利用率。
源码入口:
SlotSharingGroup
、ExecutionJobVertex#slotSharingGroup
口诀速记:
一槽多子任务,资源用得满。
2. Operator Chaining机制
定义:
Flink会将可以合并的算子链(如map→filter→flatMap)在同一个线程中执行,减少线程切换和数据序列化开销,提高执行效率。
原理细节:
- 默认开启,除非调用
disableChaining()
。 - Chaining后的算子SubTask会被分配到同一个Slot和同一线程中。
- 只有算子之间没有shuffle、rebalance等操作时才能Chaining。
源码入口:
StreamGraph#configureOperatorChain
口诀速记:
能链则链,省Slot省线程。
六、动态调整并行度
Flink支持作业级和算子级的动态并行度调整,尤其是在Savepoint和Rescale场景下。
1. Savepoint恢复时调整
- 通过Savepoint恢复作业时,可指定新的并行度,Flink会自动做状态重分布。
- 命令行示例:
./bin/flink run -s <savepoint-path> -p 8 myjob.jar
2. Flink 1.10+支持Rescale
- 支持运行时对作业进行Rescale(动态增减并行度),无需停止整个作业。
3. 注意事项
- 算子的最大并行度(maxParallelism)一旦设定,不能小于历史的maxParallelism。
- 状态后端(如RocksDBStateBackend)更适合大状态量的动态并行度调整。
口诀速记:
Savepoint调并行,状态要兼容。
七、常见并行度配置问题及排查
1. 并行度设置过大
- 现象:作业一直处于等待资源分配(SCHEDULED)。
- 排查:检查集群总Slot数是否小于作业最大并行度。
2. 并行度设置过小
- 现象:CPU利用率低,处理能力不足。
- 排查:适当提升并行度,或增加TaskManager/Slot数量。
3. Slot未均衡分配
- 现象:部分TaskManager很忙,部分空闲。
- 排查:合理设置TaskManager数量和Slot数,避免资源倾斜。
4. Chaining导致无法单独监控算子
- 现象:多个算子合成一条链,难以单独监控每个算子的指标。
- 排查:必要时通过
disableChaining()
隔离算子。
5. 算子状态兼容性问题
- 现象:调整并行度后作业恢复失败。
- 排查:确认maxParallelism参数一致,且状态后端支持重分布。
口诀速记:
Slot不够等资源,Slot太多浪费钱。链太多难分辨,调并行看状态。
八、总结与实践建议
- 理解架构:JobManager调度,TaskManager执行,Slot是资源单元。
- 熟悉提交流程:作业提交、调度、分配、执行、容错全链路。
- 科学设置并行度:既不过低浪费资源,也不过高造成排队。
- Slot与并行度匹配:并行度 ≤ 总Slot数,合理分布提升性能。
- 源码可溯源:关键分配逻辑可通过源码追踪理解。
- 口诀速记:
- 架构:Job管调度,Task管执行,Slot管资源,SubTask落其上。
- 流程:客户端提交,JobManager调度,ResourceManager分配,TaskManager执行。
- 并行度:全局定默认,算子可覆盖,Slot定上限,核数做参考。
九、全局口诀速记总结
Job管调度,Task管执行,Slot分资源,SubTask落其上;
并行度看Slot,Slot看核数,全局可默认,特殊可覆盖;
一槽多子任务,能链则链,Savepoint调并行,状态要兼容。
十、推荐阅读
- Flink官方文档:并行度与资源管理
- Flink源码分析系列
- Flink Web UI使用指南
如需深入探讨Flink资源管理、Slot Sharing、动态扩缩容等高级话题,欢迎留言交流!