Flink AM 启动链路解析

文章目录

    • 前言
    • [1. 先搞清楚这栋楼长什么样](#1. 先搞清楚这栋楼长什么样)
      • [1.1 五层架构:一栋楼从门口到工位](#1.1 五层架构:一栋楼从门口到工位)
      • [1.2 Application 模式 vs Per-Job 模式](#1.2 Application 模式 vs Per-Job 模式)
    • [2. YARN 层面:AM 是怎么"生"出来的](#2. YARN 层面:AM 是怎么"生"出来的)
      • [2.1 八步启动流程](#2.1 八步启动流程)
      • [2.2 AM 启动命令长什么样](#2.2 AM 启动命令长什么样)
    • [3. AM 内部:一个 JVM 的自我修养](#3. AM 内部:一个 JVM 的自我修养)
      • [3.1 九步内部启动流程](#3.1 九步内部启动流程)
      • [3.2 组件启动顺序:为什么一步都不能乱](#3.2 组件启动顺序:为什么一步都不能乱)
    • [4. 核心类源码:挨个过堂](#4. 核心类源码:挨个过堂)
      • [4.1 YarnApplicationClusterEntrypoint:入口类](#4.1 YarnApplicationClusterEntrypoint:入口类)
      • [4.2 ClusterEntrypoint:模板方法模式现场](#4.2 ClusterEntrypoint:模板方法模式现场)
      • [4.3 YarnResourceManager:资源包工头](#4.3 YarnResourceManager:资源包工头)
      • [4.4 ApplicationDispatcherBootstrap:反射你的 main](#4.4 ApplicationDispatcherBootstrap:反射你的 main)
    • [5. 整条链路:从一条命令到跑起来](#5. 整条链路:从一条命令到跑起来)
    • [6. 配置、翻车现场和保命建议](#6. 配置、翻车现场和保命建议)
      • [6.1 AM 关键配置参数](#6.1 AM 关键配置参数)
      • [6.2 六个翻车现场](#6.2 六个翻车现场)
      • [6.3 六条保命建议](#6.3 六条保命建议)

P.S. 目前国内还是很缺AI人才的,希望更多人能真正加入到AI行业,共同促进行业进步,增强我国的AI竞争力。想要系统学习AI知识的朋友可以看看我精心打磨的教程传送门http://blog.csdn.net/jiangjunshow,教程通俗易懂,高中生都能看懂,还有各种段子风趣幽默,从深度学习基础原理到各领域实战应用都有讲解,我22年的AI积累全在里面了。注意,教程仅限真正想入门AI的朋友,否则看看零散的博文就够了。

前言

凌晨两点,群消息响了:"作业挂了,帮我看看。"

你睡眼惺忪打开 YARN 界面,看到 AM 容器被杀了。说实话,你不太清楚一条命令是怎么变成一个进程的,只能盯着日志发呆。

别急,今天就把这条链从头到尾捋一遍。看完不敢保证你能当架构师,但下次背锅,你能背得有理有据。

1. 先搞清楚这栋楼长什么样

1.1 五层架构:一栋楼从门口到工位

整个系统分成五层:客户端、YARN、ApplicationMaster、Flink 作业组件、TaskManager。

你把它想象成一栋写字楼就行:客户端是前台,只管收快递;YARN 是物业,管工位和电费;AM 是你刚入职的工位;JobMaster 是项目经理;TaskManager 是楼下干活的实习生。

客户端层 :CliFrontend 解析 flink run-application 命令,YarnClusterDescriptor 负责部署集群,YarnClient 负责向 YARN 提交应用。客户端只上传 Jar、只提交,不执行你的 main------它就是前台,别指望它帮你干活,它连你的代码都不看一眼。

YARN 层:ResourceManager 管全局资源,分配 AM 容器和 TM 容器;NodeManager 负责启动、监控容器。说白了,物业只关心你在哪办公、电费交没交,至于你跑的是 Flink 还是别的,它才不管。

ApplicationMaster 层:这是 YARN 容器里跑的一个 JVM 进程,入口类是 YarnApplicationClusterEntrypoint,里面装着 YarnResourceManager、ApplicationDispatcher、WebMonitorEndpoint。一个 JVM 装下半个公司,比创业公司还紧凑。

Flink 作业组件层:AM 内部为每个作业启动 JobManagerRunner,JobManagerRunner 再启动 JobMaster,JobMaster 构建 ExecutionGraph 并调度 Task。注意,Application 模式下你的 main 是在 AM 端执行的------对,你的代码跑在别人家服务器上,像极了外包。

TaskManager 层:YARN 容器里跑的 TaskExecutor,入口类 YarnTaskExecutorRunner,管 Slot、执行 Task、向 RM 心跳上报。实习生干最多的活,拿最少的关注。

1.2 Application 模式 vs Per-Job 模式

这俩模式都为每个作业起独立集群,最大区别只有一件事:你的 main 在哪执行。

Per-Job 模式:客户端执行你的 main,构建好 JobGraph 再序列化传给 AM。相当于你去餐厅吃饭,还得自己拎着菜进后厨炒,炒完再端出来。

Application 模式:客户端只上传 Jar,main 在 AM 端执行,JobGraph 也在 AM 端构建。相当于你报个菜名,后厨全包了,你就负责等菜。

对比维度 Application 模式(推荐) Per-Job 模式(已废弃)
AM 入口类 YarnApplicationClusterEntrypoint YarnJobClusterEntrypoint
main 执行位置 AM 端执行 客户端执行
JobGraph 构建 AM 端构建 客户端构建
客户端负载 低(只上传 Jar) 高(构建 JobGraph)
Dispatcher ApplicationDispatcher(单应用) JobDispatcher(单作业)
多作业支持 支持单应用内多作业 不支持
提交命令 flink run-application -t yarn-application flink run -t yarn-per-job

Per-Job 又累又没人爱,Flink 1.15 直接把它废弃了。它就像那种还在跑的祖传代码:没人敢碰,但官方已经官宣"别用了"。

生产环境认准 Application 模式:客户端轻、能访问集群本地资源、支持多作业。以后谁再让你用 Per-Job,你先问问他是不是还想用 EJB。

2. YARN 层面:AM 是怎么"生"出来的

2.1 八步启动流程

第1步:客户端创建 YarnApplication

  • YarnClusterDescriptor.deployApplicationCluster() 被调用
  • 创建 YarnClient,连接 YARN ResourceManager
  • 调用 yarnClient.createApplication() 获取新应用 ID(ApplicationId)

跟医院挂号似的,先领个号,没号后面全白搭。

第2步:上传资源到 HDFS

  • 上传 Flink Jar(flink-dist.jar)到 HDFS 临时目录
  • 上传用户 Jar 到 HDFS
  • 上传配置文件(flink-conf.yaml、log4j.properties)到 HDFS
  • 设置资源本地化(LocalResource):HDFS 路径、大小、时间戳、类型(FILE/ARCHIVE)

这就是托运,行李先走,人还没上飞机,箱子已经在地面等着了。

第3步:提交应用到 YARN RM

  • 构建 ApplicationSubmissionContext
  • 设置 AM 启动命令(ContainerLaunchContext)
  • 设置 AM 资源(内存、CPU)
  • 设置队列、优先级、应用名称
  • 调用 yarnClient.submitApplication(context) 提交到 YARN RM

简历投出去了,能不能拿到面试通知,看缘分。

第4步:YARN RM 分配 AM 容器

  • YARN RM 调度器(Capacity Scheduler / Fair Scheduler)接收应用
  • 根据队列资源、优先级、用户限制调度
  • 在某个 NodeManager 上分配 AM Container
  • 向该 NM 发送 ContainerLaunch 事件

HR 终于看完了你的简历,开始给你分配工位。

第5步:NodeManager 启动 AM 容器

  • NM 接收 ContainerLaunch 事件
  • 从 HDFS 下载本地化资源(Jar、配置)
  • 设置环境变量(JAVA_HOME、HADOOP_CONF_DIR、FLINK_CONF_DIR 等)
  • 构建 AM 启动命令(java -cp ... YarnApplicationClusterEntrypoint ...)
  • 启动 AM 容器进程(JVM)

物业开门、通电、喊你入职,工位擦得锃亮。

第6步:AM 注册到 YARN RM

  • AM JVM 启动后,YarnResourceManager 创建 AMRMClient
  • 调用 amrmClient.registerApplicationMaster(host, rpcPort, trackingUrl)
  • 上报 Host、RPC 端口、Web UI 跟踪 URL
  • YARN RM 将应用状态从 ACCEPTED 改为 RUNNING

入职第一件事:到人事部报到,不然系统里没你这号人。

第7步:AM 向 RM 申请 TM 容器

  • YarnResourceManager 根据 Slot 需求创建 ContainerRequest
  • 调用 amrmClient.addContainerRequest(containerRequest)
  • 指定资源(内存、CPU)、优先级、节点标签

翻译成人话:跟老板要实习生,还指定了学历和身高。

第8步:YARN RM 分配 TM 容器,NM 启动 TM

  • YARN RM 调度器分配 TM Container
  • NM 下载资源,启动 TM 容器(YarnTaskExecutorRunner)
  • TM 启动后向 Flink ResourceManager 注册 Slot
  • JobMaster 部署 Task 到 TM Slot,作业开始运行

实习生到位,项目开工。前七步全是铺垫,就等这一步。

2.2 AM 启动命令长什么样

YARN 自动生成的 AM 容器启动命令大概长这样。不用背,但面试官一定会问:

bash 复制代码
$JAVA_HOME/bin/java -Xmx1024m -Xms1024m \
  -classpath $HADOOP_CONF_DIR:$FLINK_LIB_DIR/*:$USER_JAR \
  -Dlog.file=$LOG_DIR/am.log \
  -Dlog4j.configuration=file:$LOG_DIR/log4j.properties \
  org.apache.flink.yarn.entrypoint.YarnApplicationClusterEntrypoint \
  --configDir $FLINK_CONF_DIR \
  --executionMode application \
  --jobClassName com.example.MyJob \
  --savepointPath hdfs:///savepoints/savepoint-xxx \
  1> $LOG_DIR/am-stdout.log 2> $LOG_DIR/am-stderr.log

关键参数就四个:

  • --configDir:Flink 配置目录
  • --executionMode application:执行模式(Application 模式)
  • --jobClassName:用户作业入口类(Application 模式在 AM 端执行 main)
  • --savepointPath:从保存点恢复(可选)

注意最后那两行重定向:标准输出和标准错误分开写。这是 YARN 版的"工作要留痕",出事了先翻 am-stderr.log,别一上来就对着 stdout 抓瞎。

3. AM 内部:一个 JVM 的自我修养

3.1 九步内部启动流程

第1步:JVM 启动,加载配置

  • YARN 启动 AM JVM 进程
  • 执行 YarnApplicationClusterEntrypoint.main(String\[\] args)
  • 解析命令行参数(--configDir、--executionMode、--jobClassName 等)
  • 加载 flink-conf.yaml 配置文件
  • 合并动态配置(YARN 传入的 -D 参数)

起床第一件事:先看天气再决定穿什么。

第2步:创建 ClusterEntrypoint

  • 创建 YarnApplicationClusterEntrypoint 实例
  • 初始化 PluginManager(加载插件)
  • 初始化 FileSystem(HDFS 等)
  • 初始化 Security(Kerberos 认证)
  • 初始化 MetricRegistry(指标注册)

穿好衣服、带好证件、戴上手表,一个都不能少。

第3步:启动 RPC 服务(AkkaRpcService)

  • 创建 AkkaRpcService,启动 ActorSystem
  • 绑定 RPC 地址(jobmanager.rpc.address,YARN 自动设为容器 hostname)
  • 绑定 RPC 端口(jobmanager.rpc.port,默认 6123)
  • 用于与 TaskManager、客户端、ResourceManager 通信

开机、联网、把手机调成响铃,不然别人找不着你。

第4步:启动 HighAvailabilityServices

  • 根据 high-availability.type 配置创建 HA 服务
  • ZooKeeper HA:创建 ZooKeeperClient、Leader 选举服务、BlobStore
  • Kubernetes HA:创建 K8s Client、ConfigMap Leader 选举
  • 用于 JobManager Leader 选举和 JobGraph 存储

给自己买保险。没买保险的 AM,挂了就是真挂了,连诈尸的机会都没有。

第5步:启动 ResourceManager(YarnResourceManager)

  • 创建 YarnResourceManager 实例
  • 创建 AMRMClient(连接 YARN RM,申请容器)
  • 创建 NMClient(管理 TaskManager 容器)
  • 启动 SlotManager(管理 Slot 分配)
  • 向 YARN RM 注册 AM(registerApplicationMaster)

当上包工头,先跟大老板握手,再跟楼下施工队对接。

第6步:启动 Dispatcher(ApplicationDispatcher)

  • 创建 ApplicationDispatcher(单应用 Dispatcher)
  • 创建 JobManagerRunnerFactory(用于启动 JobManagerRunner)
  • 初始化已恢复作业(从 HA 存储恢复)
  • 启动 ApplicationDispatcherBootstrap(Application 模式特有,执行用户 main)

当上接线员,所有作业都得从你这转接。

第7步:启动 REST 端点(WebMonitorEndpoint)

  • 创建 WebMonitorEndpoint(基于 Netty 的 REST 服务器)
  • 绑定 rest.port(默认 8081)
  • 注册所有 REST 处理器(作业提交、状态查询、指标、配置等)
  • 启动 Web UI 静态资源服务
  • 客户端通过 REST API 提交作业、查询状态

挂上招牌:欢迎光临,网页可查,童叟无欺。

第8步:执行用户 main 方法(Application 模式特有)

  • ApplicationDispatcherBootstrap 通过反射执行用户 main(String\[\] args)
  • 用户代码创建 StreamExecutionEnvironment
  • 添加 Source、Transformation、Sink
  • 调用 env.execute() 触发 StreamGraph→JobGraph 构建
  • JobGraph 提交给 Dispatcher(本地提交,不走 REST)

你写了十年 Java,绝对想不到有朝一日你的 main 会被别人用反射调用,跟查户口似的:加载你的类、拿你的方法、直接 invoke。

第9步:启动 JobManagerRunner → JobMaster,作业运行

  • Dispatcher 为每个作业创建 JobManagerRunnerImpl
  • JobManagerRunner 进行 Leader 选举(HA 模式)
  • Leader 选举成功后启动 JobMaster
  • JobMaster 构建 ExecutionGraph(JobGraph→ExecutionGraph)
  • 向 ResourceManager 申请 Slot
  • TaskManager 注册 Slot 后,JobMaster 部署 Task 到 Slot
  • Task 启动,从 Source 开始消费数据,作业正式运行

正式开工。前八步全是在搭台,第九步才轮到唱戏。

3.2 组件启动顺序:为什么一步都不能乱

arduino 复制代码
PluginManager → FileSystem → Security → MetricRegistry → AkkaRpcService → HighAvailabilityServices → ResourceManager → Dispatcher → WebMonitorEndpoint → 用户 main → JobManagerRunner → JobMaster → TaskManager 注册 → 作业运行

为什么是这个顺序?一句话:谁被依赖,谁先上。

  • 基础组件(Plugin/FileSystem/Security/Metric)必须最先启动,后面全指望它们,像水电燃气。
  • AkkaRpcService 必须在 HA 和 RM 之前,因为它俩都要打电话。
  • HA 服务必须在 RM 和 Dispatcher 之前,因为需要 Leader 选举和 JobGraph 存储。
  • ResourceManager 必须在 Dispatcher 之前,因为 Dispatcher 启动 JobMaster 后要向 RM 申请 Slot。
  • Dispatcher 必须在 REST 端点之前,因为 REST 端点要拿 Dispatcher 交作业。
  • 用户 main 在 Dispatcher 启动后执行,因为要有人接住 JobGraph。
  • JobMaster 等用户 main 提交 JobGraph 后才启动。
  • TaskManager 在 RM 启动后注册,JobMaster 才能部署 Task。

这个顺序比你去医院体检的路线还严格,乱一步整栋楼就塌。你敢把 Dispatcher 提到 RM 前面,JobMaster 起来发现没人给 Slot,当场表演一个"要啥没啥"。

4. 核心类源码:挨个过堂

4.1 YarnApplicationClusterEntrypoint:入口类

java 复制代码
public class YarnApplicationClusterEntrypoint extends YarnJobClusterEntrypoint {

    public static void main(String[] args) {
        // 1. 解析命令行参数
        final CommandLineParser<YarnApplicationClusterConfiguration> parser =
            new CommandLineParser<>(new YarnApplicationClusterConfigurationParserFactory());
        YarnApplicationClusterConfiguration configuration = null;
        try {
            configuration = parser.parse(args);
        } catch (Exception e) {
            System.exit(1);
        }

        // 2. 安装默认文件系统
        installDefaultFileSystem(configuration.getConfigDir());

        // 3. 加载配置
        final Configuration config = loadConfiguration(configuration.getConfigDir());

        // 4. 创建并启动 ClusterEntrypoint
        final YarnApplicationClusterEntrypoint entrypoint =
            new YarnApplicationClusterEntrypoint(config, configuration);
        entrypoint.startCluster();
    }

    @Override
    protected Dispatcher createDispatcher(
            Configuration configuration,
            RpcService rpcService,
            HighAvailabilityServices haServices,
            ResourceManagerGateway resourceManagerGateway,
            BlobServer blobServer,
            HeartbeatServices heartbeatServices,
            JobManagerMetricGroup jobManagerMetricGroup,
            ExecutorService executorService) throws Exception {
        // 创建 ApplicationDispatcher(单应用)
        final ApplicationDispatcherBootstrap bootstrap =
            new ApplicationDispatcherBootstrap(
                applicationConfiguration.getJobClassName(),
                applicationConfiguration.getProgramArgs(),
                applicationConfiguration.getSavepointPath());

        return new ApplicationDispatcher(
            rpcService,
            haServices.getDispatcherLeaderElection(),
            ...
            bootstrap);
    }

    @Override
    protected ResourceManager<?> createResourceManager(
            Configuration configuration,
            ResourceID resourceId,
            RpcService rpcService,
            HighAvailabilityServices haServices,
            HeartbeatServices heartbeatServices,
            ...) throws Exception {
        // 创建 YarnResourceManager
        return YarnResourceManager.create(
            configuration,
            resourceId,
            rpcService,
            haServices,
            heartbeatServices,
            ...);
    }
}

关键逻辑:

  • main 方法就是 AM 进程的入口:解析参数、装默认文件系统、加载配置、创建并 startCluster,一气呵成。
  • createDispatcher 创建 ApplicationDispatcher,顺手塞一个 ApplicationDispatcherBootstrap------这个 bootstrap 就是专门执行你 main 的"工具人"。
  • createResourceManager 创建 YarnResourceManager,负责跟 YARN RM 申请容器。
  • 它继承自 YarnJobClusterEntrypoint,把 YARN 通用逻辑全复用了一遍。能继承的绝不重写,这觉悟值得大多数程序员学习。

4.2 ClusterEntrypoint:模板方法模式现场

java 复制代码
public abstract class ClusterEntrypoint implements AutoCloseable {

    private final Configuration configuration;
    private PluginManager pluginManager;
    private FileSystem fileSystem;
    private RpcService rpcService;
    private HighAvailabilityServices haServices;
    private ResourceManager<?> resourceManager;
    private Dispatcher dispatcher;
    private WebMonitorEndpoint<?> webMonitorEndpoint;

    public void startCluster() throws Exception {
        try {
            // 1. 初始化基础组件
            pluginManager = PluginUtils.createPluginManager(...);
            FileSystem.initialize(configuration, pluginManager);
            SecurityUtils.install(new SecurityConfiguration(configuration));
            MetricRegistry metricRegistry = createMetricRegistry(configuration);

            // 2. 启动 RPC 服务
            rpcService = createRpcService(configuration);

            // 3. 启动 HA 服务
            haServices = createHaServices(configuration, rpcService);

            // 4. 启动 BlobServer(Jar 存储)
            BlobServer blobServer = new BlobServer(configuration, haServices.createBlobStore());

            // 5. 启动 ResourceManager
            resourceManager = createResourceManager(...);
            resourceManager.start();

            // 6. 启动 Dispatcher
            dispatcher = createDispatcher(...);
            dispatcher.start();

            // 7. 启动 REST 端点
            webMonitorEndpoint = createRestEndpoint(...);
            webMonitorEndpoint.start();
        } catch (Exception e) {
            throw new FlinkException("Could not start cluster entrypoint.", e);
        }
    }

    // 抽象方法,由子类实现
    protected abstract Dispatcher createDispatcher(...);
    protected abstract ResourceManager<?> createResourceManager(...);
    protected abstract WebMonitorEndpoint<?> createRestEndpoint(...);
}

关键设计:

  • startCluster 把标准的七步启动流程写死,模板方法模式的教科书现场。
  • 基础组件最先,然后 RPC → HA → BlobServer → ResourceManager → Dispatcher → REST 端点,顺序锁死。
  • createDispatcher / createResourceManager / createRestEndpoint 全是抽象方法,留给子类填空。
  • 跟考试填空题一样:框架给你,答案自己填,填错全校通报------启动失败抛异常,AM 进程退出,YARN 标记应用失败。

4.3 YarnResourceManager:资源包工头

java 复制代码
public class YarnResourceManager extends ActiveResourceManager<YarnWorkerNode> {

    private final AMRMClient<ContainerRequest> amrmClient;
    private final NMClient nmClient;
    private final int taskManagerMemory;
    private final int taskManagerCPU;

    public static YarnResourceManager create(...) throws Exception {
        // 1. 创建 AMRMClient(连接 YARN RM)
        AMRMClient<ContainerRequest> amrmClient = AMRMClient.createAMRMClient();
        amrmClient.init(conf);
        amrmClient.start();

        // 2. 创建 NMClient(管理 TM 容器)
        NMClient nmClient = NMClient.createNMClient();
        nmClient.init(conf);
        nmClient.start();

        return new YarnResourceManager(..., amrmClient, nmClient);
    }

    @Override
    protected void initialize() throws ResourceManagerException {
        // 3. 向 YARN RM 注册 AM
        amrmClient.registerApplicationMaster(
            rpcService.getAddress(),
            rpcService.getPort(),
            webMonitorUrl);
    }

    @Override
    public CompletableFuture<Void> requestNewWorker(WorkerResourceSpec workerResourceSpec) {
        // 4. 向 YARN RM 申请 TM 容器
        final ContainerRequest containerRequest = new ContainerRequest(
            Resource.newInstance(taskManagerMemory, taskManagerCPU),
            null, // 节点(null 表示任意节点)
            null, // 机架
            Priority.newInstance(0));
        amrmClient.addContainerRequest(containerRequest);
        return CompletableFuture.completedFuture(null);
    }

    @Override
    public void onContainersAllocated(List<Container> containers) {
        // 5. YARN RM 分配容器回调
        for (Container container : containers) {
            // 6. 在 NM 上启动 TM 容器
            ContainerLaunchContext ctx = createTaskManagerLaunchContext();
            nmClient.startContainer(container, ctx);
        }
    }
}

关键逻辑:

  • create 创建 AMRMClient(连 YARN RM)和 NMClient(管容器):一个向上要资源,一个向下发任务。中间商不赚差价,只负责跑腿。
  • initialize 里 registerApplicationMaster 报到,正式在 YARN 挂上号。
  • requestNewWorker 构造 ContainerRequest,内存 CPU 写死,节点和机架留 null------意思是不挑地方,哪都行。
  • onContainersAllocated 是分配回调,容器到手直接 startContainer。人狠话不多,拿到容器就开工。
  • TM 启动后向 Flink ResourceManager 注册 Slot,资源闭环完成。

4.4 ApplicationDispatcherBootstrap:反射你的 main

java 复制代码
public class ApplicationDispatcherBootstrap implements DispatcherBootstrap {

    private final String jobClassName;
    private final String[] programArgs;
    private final String savepointPath;

    public ApplicationDispatcherBootstrap(
            String jobClassName,
            String[] programArgs,
            String savepointPath) {
        this.jobClassName = jobClassName;
        this.programArgs = programArgs;
        this.savepointPath = savepointPath;
    }

    @Override
    public void initialize(DispatcherGateway dispatcherGateway, FatalErrorHandler fatalErrorHandler) {
        try {
            // 1. 加载用户作业类
            Class<?> mainClass = Class.forName(
                jobClassName, true, Thread.currentThread().getContextClassLoader());

            // 2. 获取 main 方法
            Method mainMethod = mainClass.getMethod("main", String[].class);

            // 3. 通过反射执行用户 main 方法
            // 用户 main 内部会调用 env.execute(),构建 JobGraph 并提交给 Dispatcher
            mainMethod.invoke(null, (Object) programArgs);
        } catch (Exception e) {
            fatalErrorHandler.onFatalError(
                new FlinkException("Failed to execute application main method.", e));
        }
    }
}

关键逻辑:

  • initialize 在 Dispatcher 启动后被调用,等一切就绪才轮到你上场。
  • Class.forName 加载你的类,getMethod 拿 main,mainMethod.invoke(null, args) 反射执行,一步到位。
  • 你的 main 内部创建 StreamExecutionEnvironment,env.execute() 构建 StreamGraph→JobGraph,然后通过本地 DispatcherGateway 提交------不走 REST,直接内存里交接,连快递费都省了。
  • main 挂了就调 fatalErrorHandler,AM 进程退出。你的 main 就是全村的希望:它行,作业就行;它炸,全村陪葬。

5. 整条链路:从一条命令到跑起来

arduino 复制代码
客户端 flink run-application  → YarnClusterDescriptor.deployApplicationCluster  → YarnClient.submitApplication  → YARN RM 分配 AM Container  → NodeManager 启动 AM JVM  → YarnApplicationClusterEntrypoint.main  → ClusterEntrypoint.startCluster(7步)  → AkkaRpcService → HAServices → YarnResourceManager → ApplicationDispatcher → WebMonitorEndpoint  → ApplicationDispatcherBootstrap.initialize(执行用户 main)  → 用户 env.execute() → JobGraph 提交  → Dispatcher 启动 JobManagerRunner  → JobMaster 构建 ExecutionGraph  → YarnResourceManager 申请 TM 容器  → TaskManager 注册 Slot  → JobMaster 部署 Task  → 作业运行

数一数,这一条链 17 个环节,比你双十一的快递链路还长。

而且快递丢了最多退款,这条链上任何一环断了,你收到的不是钱,是一封 "Application failed" 的邮件,外加群里那句:"帮忙看看。"

6. 配置、翻车现场和保命建议

6.1 AM 关键配置参数

yaml 复制代码
# AM 进程总内存(生产建议 2-4G)
jobmanager.memory.process.size: 2048m
# AM RPC 地址(YARN 自动设置为容器 hostname)
jobmanager.rpc.address: auto
# AM RPC 端口
jobmanager.rpc.port: 6123
# REST API / Web UI 端口
rest.port: 8081
# REST 绑定端口范围(避免端口冲突)
rest.bind-port: 8081-8090
# HA 类型(zookeeper/kubernetes/none)
high-availability.type: zookeeper
# ZooKeeper 集群地址
high-availability.zookeeper.quorum: zk1:2181,zk2:2181,zk3:2181
# HA 元数据存储路径
high-availability.storageDir: hdfs:///flink/ha
# YARN 队列名称
yarn.application.queue: default
# YARN 应用名称
yarn.application.name: Flink App
# 每个容器虚拟核数
yarn.containers.vcores: auto
# YARN 应用优先级
yarn.application.priority: 0
# Blob 服务端口(Jar 传输)
blob.server.port: 随机
# Blob 本地存储目录
blob.storage.directory: /tmp/blob
# 故障恢复策略(region/full)
jobmanager.execution.failover-strategy: region

这么多参数,记住三件事就够:

  • jobmanager.memory.process.size 生产给 2-4G,别抠。
  • rest.bind-port 留个范围 8081-8090,不然端口打架。
  • HA 必须配。没 HA 的 AM,挂了就是真挂了,连诈尸的机会都没有------这话我说第二遍了,说明真的很重要。

6.2 六个翻车现场

1. AM 容器启动失败(Container killed by YARN)

原因:AM 内存超 YARN 容器限制,或 AM 进程 OOM 被 YARN 杀了。

解法:调大 jobmanager.memory.process.size,检查 YARN 容器内存限制,翻 AM 日志找 OOM。

翻译:给工位换张更大的桌子。

2. AM 启动后无法连接 YARN RM

原因:YARN 配置错误、Kerberos 认证失败、网络不通。

解法:检查 yarn-site.xml、确认 Kerberos ticket、检查网络连通性,翻 AM 日志看连接错误。

翻译:报到那天人事部没人,你在大厅干等。

3. 用户 main 方法执行失败(ClassNotFoundException)

原因:用户 Jar 没上传到 HDFS,或依赖缺失,或加载顺序错误。

解法:确认 Jar 上传,用 maven-shade 打包,设置 classloader.resolve-order=child-first。

翻译:菜谱里写的食材,后厨一样都没有。

4. REST API 无法访问(Connection refused)

原因:rest.port 被占、AM 没绑到正确地址、防火墙拦截。

解法:设置 rest.bind-port 端口范围、检查 rest.address、检查防火墙规则。

翻译:招牌挂了,但客人进不来。

5. TaskManager 无法注册到 ResourceManager

原因:TM 连不上 AM RPC 地址、Slot 不匹配、TM 内存不足。

解法:检查 jobmanager.rpc.address、确认 TM 网络可达、检查 taskmanager.numberOfTaskSlots、加大 TM 内存。

翻译:实习生到楼下报到,前台说系统里没这人。

6. ZooKeeper Leader 选举失败

原因:ZK 连接失败、session 超时、ACL 权限问题。

解法:检查 high-availability.zookeeper.quorum、确认 ZK 集群正常、检查 zk-path-prefix 和 ACL。

翻译:几个领导抢一个话筒,谁也没抢到。

六个问题凑齐,可以召唤运维了。其实大半是配置和网络的事,跟写代码没半毛钱关系------你只是那个负责背锅的。

6.3 六条保命建议

  1. 合理设置 AM 内存:生产建议 2-4G,大作业或多作业的 Application 模式适当加大,别让 AM OOM 带崩整个应用。
  2. 配置 HA 高可用:生产必须配 ZooKeeper 或 Kubernetes HA,设好 high-availability.storageDir,确保 AM 挂了能自己爬起来。
  3. 使用 Application 模式:客户端轻量化,main 在 AM 端执行,Per-Job 已废弃,别再用它感动自己。
  4. 配置 REST 端口范围:rest.bind-port=8081-8090,避免端口冲突;生产环境加负载均衡或反向代理访问 Web UI。
  5. 监控 AM 日志和指标:盯 YARN container logs、JVM 内存、GC、线程数,Prometheus 安排上。
  6. 配置 YARN 队列和优先级:重要作业用独立队列,设 yarn.application.priority,别跟别人抢资源。

最后总结一句人话:YARN 管"在哪跑",Flink 管"跑什么",分层协作,谁也不越界------像极了产品经理和研发互相甩锅,但最后一起背锅。

理解这条启动链路,最大的收获不是面试能吹,而是作业挂了的时候,你能在五分钟内定位到是哪个环节的问题,而不是对着 YARN 界面发呆半小时,最后截图发群里,配一句:"像是网络问题。"

P.S. 目前国内还是很缺AI人才的,希望更多人能真正加入到AI行业,共同促进行业进步,增强我国的AI竞争力。想要系统学习AI知识的朋友可以看看我精心打磨的教程 传送门http://blog.csdn.net/jiangjunshow,教程通俗易懂,高中生都能看懂,还有各种段子风趣幽默,从深度学习基础原理到各领域实战应用都有讲解,我22年的AI积累全在里面了。注意,教程仅限真正想入门AI的朋友,否则看看零散的博文就够了。

相关推荐
LCD液晶屏1 小时前
立煌科技代理京东方液晶屏的核心优势与应用价值
大数据·人工智能·科技
waterfeeling6 小时前
用 Context7 做一次点云配准的调研—测试—报告闭环:鲁棒 ICP
人工智能
Data-Miner10 小时前
面对 AI 大模型智能涌现,如何从技术层面保护好隐私与数据安全?
大数据·人工智能
hpoenixf10 小时前
当 AI 开始谈“买什么”,研究助手离投顾和交易还有多远?
人工智能
大模型真好玩10 小时前
如何让视频模型对你言听计从?结构化提示词助你生成电影级画面
人工智能·agent·deepseek
zhang1338308907510 小时前
传统机械三杯式风速传感器易磨损冻堵?CG-88金属超声波风速风向传感器给出免运维方案
运维·服务器·网络·人工智能·自动化
特立独行的猫a10 小时前
会说确认叫车才下单-启元机器人官方滴滴打车技能源码详解
人工智能·机器人·技能·启元机器人
老金带你玩AI11 小时前
GPT-6.1 Terra上线:更便宜了,干活到底怎么样?
人工智能
会议咨询11 小时前
2027年电力电子、人工智能与大数据国际会议(PEABD 2027)
大数据·人工智能·电力电子