Flink基础之Per-Job-Cluster原理详解:被 Application 取代的模式

摘要

讲透 Flink on YARN 中 Per-Job 模式的完整原理:每作业一个临时集群的架构、两阶段启动与提交流程、main() 在本地 Client 执行的三个后果,以及它为何被 Application 模式取代的演进逻辑;并给出配置命令、老集群迁移要点与四个真实踩坑点。

关键词

Flink、Per-Job、yarn-per-job、临时集群、Application Master、main() 本地执行、两阶段启动、废弃、Application


Flink on YARN 的三种模式里,Session 和 Application 都还在被广泛使用,唯独 Per-Job 夹在中间,处境尴尬:它既有 Application 的「每作业一集群」隔离性,又保留了 Session 的「main() 在本地执行」------然后被官方在 1.15 标记废弃。为什么一个「看起来两头都占」的模式会走向终结?这一篇讲透它的原理,答案自然就出来了。

理解 Per-Job 还有个现实意义:很多老集群(Flink 1.14 及以前)的存量作业还在以 per-job 方式运行,迁移前必须知道它内部怎么工作。


一、Per-Job Cluster 架构:每作业一个临时集群

Per-Job 的架构一句话能概括:每次 flink run 都向 YARN 申请一个专用集群,作业结束(或失败放弃)集群随即销毁

架构要点:

  • 本地 Client 执行用户 main()(与 Session 相同),生成 StreamGraph → JobGraph。
  • AM 容器内运行 JobManager,只服务这一个作业,无共享、无竞争。
  • TaskManager 是本作业专属的,资源独享,背压和状态问题不会传导给其他作业。
  • 集群生命周期与作业严格绑定:提交时创建,作业结束时销毁,YARN 资源随之回收。

它的隔离性其实和 Application 一样好------只是实现路径绕了一个弯:先让 YARN 把集群起来,再让本地代码把作业塞进去


二、启动与提交流程:两阶段模式

Per-Job 的提交是两阶段串行的,这也是它启动最慢的根源:

阶段一:先起临时集群

  1. Client 把 flink-dist JAR 和配置上传到 HDFS;
  2. 向 YARN RM 提交应用(申请一个临时集群);
  3. RM 选择一个 NM 启动 AM,AM 内启动 JobManager 框架;
  4. 集群就绪------此时还没有任何用户代码,集群在「空等」

阶段二:本地跑代码并提交作业

  1. Client 在本地执行用户 main(),生成 JobGraph;
  2. 通过 REST 把 JobGraph 提交到新集群的 Dispatcher;
  3. Flink 的 ResourceManager 申请本作业专属的 TaskManager 容器;
  4. 作业运行;结束时集群销毁。

两阶段的核心问题是串行等待:起集群要等 YARN 分配 AM 容器,这段等待发生在任何用户代码执行之前。更糟的是,如果第 5 步 main() 在本地抛异常(缺依赖、配置错),集群刚起来就白起了,一次资源申请白白浪费。


三、main() 在本地执行的三个后果

这是 Per-Job 与 Application 唯一的本质差异,也是它所有问题的来源:

  1. 提交机的 classpath 必须完整 。main() 在本地跑,flink-connector-kafka、自定义依赖等必须在提交机上有。缺了就是 NoClassDefFoundError,而且发生在集群已经启动之后。
  2. 提交机成为作业链路的故障点。main() 执行期间提交机断网、宕机、进程被杀,作业直接提交失败或半途而废。
  3. 本地环境与集群环境脱节。本地的 Flink 版本、依赖版本必须与集群严格兼容,否则序列化对不上------这是老集群最常见的提交报错来源。

这三个后果在 Application 模式下全部消失:main() 搬进 AM 后,提交机退化成纯粹的「JAR 上传者」。


四、Per-Job vs Application:一字之差,差在哪

两者都是「每作业一个集群」,对比下来唯一本质区别就是 main() 的位置:

维度 Per-Job Application
集群生命周期 每作业独立 每作业独立
main() 位置 本地 Client 集群内 AM
启动方式 两阶段串行 单阶段并行
本地依赖 必须完整 只传 JAR
状态 1.15 起废弃 生产推荐

结论很直接:Application = Per-Job 的「main() 搬家」------把本地执行的代码搬进集群,其余机制全部保留。这不是革命,是一次精准的演进。


五、为什么被废弃

结合上面的分析,官方废弃 Per-Job 的理由很清晰:

  1. main() 在本地执行是设计缺陷:把提交机变成依赖点、故障点,与现代「提交即分离」的运维理念冲突。
  2. 两阶段启动又慢又浪费:起集群空等 + main() 失败白起,资源与时间双损耗。
  3. 功能被 Application 完全覆盖:Application 保留 Per-Job 全部优点(每作业隔离),同时消除全部缺点------没有理由再维护两套实现。

所以它不是「不好用才被废弃」,而是「更好的替代品出现后被收编」。Flink 1.15 的 release notes 里明确建议:新作业一律使用 run-application


六、配置与命令

bash 复制代码
# Flink 1.14 及以前:per-job 提交
bin/flink run -t yarn-per-job -d -c com.example.MainClass ./app.jar

# Flink 1.15+:同一个作业迁移到 Application
bin/flink run-application -t yarn-application -c com.example.MainClass ./app.jar

# 查看 per-job 应用(YARN 上每个作业是一个 application)
yarn application -list
yarn application -kill <applicationId>
yaml 复制代码
# flink-conf.yaml(两种模式通用)
jobmanager.memory.process.size: 2048m
taskmanager.memory.process.size: 4096m
taskmanager.numberOfTaskSlots: 4
# 作业失败时集群行为:per-job 下作业结束即销毁集群

老集群迁移到 Application 的注意点:提交命令从 flink run -t yarn-per-job 换成 flink run-application -t yarn-application,JAR 的依赖打包方式不变,但要检查 main() 里是否依赖了本地文件路径或环境变量------这些在 Application 模式下是拿不到的。


七、四个真实踩坑

  1. 迁移后 main() 里的本地路径失效 。Per-Job 的 main() 在提交机跑,new File("/opt/conf/rule.json") 能读到;Application 的 main() 在 AM 里跑,这个路径在集群节点上不存在。迁移时必须把这类资源改为打包进 JAR 或放 HDFS。
  2. 升级 Flink 版本时把 per-job 命令照搬 。1.15+ 的 flink run -t yarn-per-job 会输出废弃警告,某些版本甚至直接报错。升级脚本里必须同步改 run-application
  3. 「提交机挂 = 作业挂」的错觉残留。Per-Job 时代养成的「提交后本地进程不能关」的习惯,在 detached(-d)模式下不成立;但很多人迁移到 Application 后仍担心这个问题------记住 Application 的提交机只是上传者,关掉无影响。
  4. 把 per-job 当 Session 用。有人以为 per-job 集群会复用,反复提交后发现每次都要等集群启动------per-job 集群是每次新建的,没有复用一说。要复用就用 Session,要隔离就用 Application,per-job 两头不靠。

Per-Job 的价值在于它是 Flink 部署模式演进史上的「中间产物」:把 Session 的共享问题解决了一半(每作业一集群),却因为 main() 留在本地引入了新的依赖问题,最终被 Application 模式收编。理解它,其实是在理解一条演进线索:从共享到隔离、从本地到集群内------Per-Job 正好站在这个转折点上,作为参照系,Session 与 Application 的差异反而看得更清楚。

相关推荐
魏 无羡8 小时前
webclient
java·webclient
大大大大晴天8 小时前
每天认识一个组件:数据治理Apache Atlas
大数据
神仙别闹8 小时前
基于 C++ 实现两个有序链表序列的交集
java·c++·链表
数字新视界9 小时前
2026模块化机房选型指南:行业市场发展趋势、占有率与竞争梯队分析报告解析
大数据·人工智能·物联网·数据中心·微模块机房·模块化机房·冷通道
swordbob9 小时前
ReentrantLock 与 AQS 完整学习手册
java·开发语言
m0_5873830010 小时前
全民健身解决方案软件开发实战:从架构设计到落地指南
java·spring boot·spring·架构·需求分析
白山编程大哥10 小时前
Java OutputStreamWriter 详解:从字符到字节的桥梁
java·开发语言·python
姜穆澜11 小时前
OneID 从 0 到 1 完整生产案例(四)
大数据
OsDepK11 小时前
项目快速Git至仓库(完整版)
大数据·git·elasticsearch·搜索引擎