Flink基于用户程序生成JobGraph,提交到集群进行分布式部署运行。本篇从源码角度讲解一下Flink Jar包是如何被提交到集群的。(本文源码基于Flink 1.11.3)

1 Flink run 提交Jar包流程分析

首先分析run脚本可以找到入口类CliFrontend,这个类在main方法中解析参数,基于第二个参数定位到run方法:

try {
// do action
switch (action) {
case ACTION_RUN:
run(params);
return 0;
case ACTION_RUN_APPLICATION:
runApplication(params);
return 0;
case ACTION_LIST:
list(params);
return 0;
case ACTION_INFO:
info(params);
return 0;
case ACTION_CANCEL:
cancel(params);
return 0;
case ACTION_STOP:
stop(params);
return 0;
case ACTION_SAVEPOINT:
savepoint(params);
return 0;
case "-h":
case "--help":
...
return 0;
case "-v":
case "--version":
...
default:
...
return 1;
}
}

在run方法中,根据classpath、用户指定的jar、main函数等信息创建PackagedProgram。在Flink中通过Jar方式提交的任务都封装成了PackagedProgram对象。

protected void run(String[] args) throws Exception {
...
final ProgramOptions programOptions = ProgramOptions.create(commandLine);
final PackagedProgram program = getPackagedProgram(programOptions); // 把用户的jar配置到config里面
final List<URL> jobJars = program.getJobJarAndDependencies();
final Configuration effectiveConfiguration = getEffectiveConfiguration(
activeCommandLine, commandLine, programOptions, jobJars); try {
executeProgram(effectiveConfiguration, program);
} finally {
program.deleteExtractedLibraries();
}
}

创建PackagedProgram后,有个非常关键的步骤就是这个effectiveConfig,这里面会把相关的Jar都放入pipeline.jars这个属性里,后面pipeline提交作业时,这些jar也会一起提交到集群。

其中比较关键的是Flink的类加载机制,为了避免用户自己的jar内与其他用户冲突,采用了逆转类加载顺序的机制。

private PackagedProgram(
@Nullable File jarFile,
List<URL> classpaths,
@Nullable String entryPointClassName,
Configuration configuration,
SavepointRestoreSettings savepointRestoreSettings,
String... args) throws ProgramInvocationException { // 依赖的资源
this.classpaths = checkNotNull(classpaths); // 保存点配置
this.savepointSettings = checkNotNull(savepointRestoreSettings); // 参数配置
this.args = checkNotNull(args); // 用户jar
this.jarFile = loadJarFile(jarFile); // 自定义类加载
this.userCodeClassLoader = ClientUtils.buildUserCodeClassLoader(
getJobJarAndDependencies(),
classpaths,
getClass().getClassLoader(),
configuration); // 加载main函数
this.mainClass = loadMainClass(
entryPointClassName != null ? entryPointClassName : getEntryPointClassNameFromJar(this.jarFile),
userCodeClassLoader);
}

在类加载器工具类中根据参数classloader.resolve-order决定是父类优先还是子类优先,默认是使用子类优先模式。

executeProgram方法内部是启动任务的核心,在完成一系列的环境初始化后(主要是类加载以及一些输出信息),会调用packagedProgram的invokeInteractiveModeForExecution的,在这个方法里通过反射调用用户的main方法。

private static void callMainMethod(Class<?> entryClass, String[] args)
throws ProgramInvocationException {
...
Method mainMethod = entryClass.getMethod("main", String[].class);
mainMethod.invoke(null, (Object) args);
...
}

执行用户的main方法后,就是flink的标准流程了。创建env、构建StreamDAG、生成Pipeline、提交到集群、阻塞运行。当main程序执行完毕,整个run脚本程序也就退出了。

总结来说,Flink提交Jar任务的流程是:
1 脚本入口程序根据参数决定做什么操作
2 创建PackagedProgram,准备相关jar和类加载器
3 通过反射调用用户Main方法
4 构建Pipeline,提交到集群

2 通过PackagedProgram获取Pipeline

有的时候不想通过阻塞的方式卡任务执行状态,需要通过类似JobClient的客户端异步查询程序状态,并提供停止退出的能力。

要了解这个流程,首先要了解Pipeline是什么。用户编写的Flink程序,无论是DataStream API还是SQL,最终编译出的都是Pipeline。只是DataStream API编译出的是StreamGraph,而SQL编译出的Plan。Pipeline会在env.execute()中进行编译并提交到集群。

既然这样,此时可以思考一个问题:Jar包任务是独立的Main方法,如何能抽取其中的用户程序获得Pipeline呢?

通过浏览源码的单元测试,发现了一个很好用的工具类:PackagedProgramUtils。

public static Pipeline getPipelineFromProgram(
PackagedProgram program,
Configuration configuration,
int parallelism,
boolean suppressOutput) throws CompilerException, ProgramInvocationException { // 切换classloader
final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(program.getUserCodeClassLoader()); // 创建env
OptimizerPlanEnvironment benv = new OptimizerPlanEnvironment(
configuration,
program.getUserCodeClassLoader(),
parallelism);
benv.setAsContext();
StreamPlanEnvironment senv = new StreamPlanEnvironment(
configuration,
program.getUserCodeClassLoader(),
parallelism);
senv.setAsContext(); try {
// 执行用户main方法
program.invokeInteractiveModeForExecution();
} catch (Throwable t) {
if (benv.getPipeline() != null) {
return benv.getPipeline();
} if (senv.getPipeline() != null) {
return senv.getPipeline();
} ...
} finally {
// 重置classloader
}
}

这个工具类首先在线程内创建了一个env,这个env通过threadload保存到当前线程中。当通过反射调用用户代码main方法时,内部的getEnv函数直接从threadlocal中获取到这个env。

ThreadLocal<StreamExecutionEnvironmentFactory> factory = new ThreadLocal<>();

public static StreamExecutionEnvironment getExecutionEnvironment() {
return Utils.resolveFactory(factory , contextEnvironmentFactory)
.map(StreamExecutionEnvironmentFactory::createExecutionEnvironment)
.orElseGet(StreamExecutionEnvironment::createLocalEnvironment);
}

再回头看看env有什么特殊的。

public class StreamPlanEnvironment extends StreamExecutionEnvironment {

    private Pipeline pipeline;

    public Pipeline getPipeline() {
return pipeline;
} @Override
public JobClient executeAsync(StreamGraph streamGraph) {
pipeline = streamGraph; // do not go on with anything now!
throw new ProgramAbortException();
}
}

原来是重写了executeAysnc方法,当用户执行env.execute时,触发异常,从而在PackagedProgramUtils里面拦截异常,获取到用户到pipeline。

总结起来流程如下:

3 编程实战

通过阅读上述源码,可以学习到:

1 classloader类加载的父类优先和子类优先问题
2 threadlocal线程级本地变量的使用
3 PackagedProgramUtils 利用枚举作为工具类
4 PackagedProgramUtils 利用重写env,拦截异常获取pipeline。

关于pipeline如何提交到集群、如何运行,就后文再谈了。

Flink源码剖析:Jar包任务提交流程的更多相关文章

  1. 转】MyEclipse使用总结——使用MyEclipse打包带源码的jar包

    原博文出自于: http://www.cnblogs.com/xdp-gacl/p/4136303.html 感谢! 平时开发中,我们喜欢将一些类打包成jar包,然后在别的项目中继续使用,不过由于看不 ...

  2. MyEclipse使用总结——使用MyEclipse打包带源码的jar包

    平时开发中,我们喜欢将一些类打包成jar包,然后在别的项目中继续使用,不过由于看不到jar包里面的类的源码了,所以也就无法调试,要想调试,那么就只能通过关联源代码的形式,这样或多或少也有一些不方便,今 ...

  3. eclipse导出附带源码的jar包

    最近在搞Andengine游戏开发,发现andengine的jar包可以直接点击查看源码,而其他项目的jar包却看不了,因此自己研究了下如何生成可以直接查看源码的jar包. 1.eclipse中点击项 ...

  4. MyEclipse打包带源码的jar包

    平时开发中,我们喜欢将一些类打包成jar包,然后在别的项目中继续使用,不过由于看不到jar包里面的类的源码了,所以也就无法调试,要想调试,那么就只能通过关联源代码的形式,这样或多或少也有一些不方便,今 ...

  5. Eclipse使用总结——使用Eclipse打包带源码的jar包

    平时开发中,我们喜欢将一些类打包成jar包,然后在别的项目中继续使用,不过由于看不到jar包里面的类的源码了,所以也就无法调试,要想调试,那么就只能通过关联源代码的形式,这样或多或少也有一些不方便,今 ...

  6. Flink 源码解析 —— Standalone Session Cluster 启动流程深度分析之 Job Manager 启动

    Job Manager 启动 https://t.zsxq.com/AurR3rN 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0到1学习 -- Mac ...

  7. Flink 源码解析 —— Standalone session 模式启动流程

    Standalone session 模式启动流程 https://t.zsxq.com/EemAEIi 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0 ...

  8. Flink 源码解析 —— Standalone Session Cluster 启动流程深度分析之 Task Manager 启动

    Task Manager 启动 https://t.zsxq.com/qjEUFau 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0到1学习 -- Ma ...

  9. Apache DolphinScheduler 源码剖析之 Worker 容错处理流程

    今天给大家带来的分享是 Apache DolphinScheduler 源码剖析之 Worker 容错处理流程 DolphinScheduler源码剖析之Worker容错处理流程 Worker容错流程 ...

  10. DolphinScheduler 源码剖析之 Master 容错处理流程

    点击上方蓝字关注 Apache DolphinScheduler Apache DolphinScheduler(incubating),简称"DS", 中文名 "海豚调 ...

随机推荐

  1. emca配置EM

    EM DC(Enterprise Manager Database Control)是 web 界面的数据库管理工具, 可用于配置 EM DC环境的工具包括: Oracle Universal In ...

  2. 线程池的介绍和使用,以及基于jvmti设计非入侵监控

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 五常大米好吃! 哈哈哈,是不你总买五常大米,其实五常和榆树是挨着的,榆树大米也好吃, ...

  3. 我用go-zero开发了第一个线上项目

    作者:结冰 前言 ​ 说在最前面,我是一个外表谦让,内心狂热,外表斯文,内心贪玩的一个普通人.我的职业是程序员,是一个golang语言爱好者,一半是因为golang好用,一半是因为其他语言学不好.我是 ...

  4. ThreadLocal使用总结

    一.ThreadLocal基本使用 1.1使用场景 不同的线程对全局变量进行修改,使用ThreadLocal后会为每个线程创建一个副本,而不会影响其他线程的副本,确保了线程的安全 private st ...

  5. [日常摸鱼]poj1151Atlantis-扫描线

    题意:给一堆长宽平行于坐标轴的长方形求并的面积 我个沙茶快写了一晚上- 大概思想就是先根据$y$坐标排个序,把$y$坐标离散化一下,放到线段树里面维护,这里的写法是让线段树的节点储存这个点对应的整段线 ...

  6. SpringCloud 的版本问题 (深入)

    学过SpringCloud 的 同学 ,就应该知道,SpringCloud是一个生态 . Spring Cloud是一个由众多独立子项目组成的大型综合项目,每个子项目都维护着自己的发布版本号.Spri ...

  7. Python小白干货宝典:sorted()函数:列表元素排序

    定义: sorted() 函数对所有可迭代的对象进行排序操作. 内建函数 sorted 方法返回的是一个新的 list,而不是在原来的基础上进行的操作. 语法: sorted 语法: sorted(i ...

  8. [论文分享] DHP: Differentiable Meta Pruning via HyperNetworks

    [论文分享] DHP: Differentiable Meta Pruning via HyperNetworks authors: Yawei Li1, Shuhang Gu, etc. comme ...

  9. 创建Web Service项目

    使用AXIS框架   idea方式: 创建后 加入axis依赖包到输出目录,idea也会提示你进行这步操作 项目启动后访问 http://localhost:8080/AxisWebService/s ...

  10. CentOS7离线安装mysql5.6

    下载mysql5.6,系统选择redhat,版本选择RHEL7,下载RPM Bundle后得到一个tar文件.这里得到文件MySQL-5.6.44-1.el7.x86_64.rpm-bundle.ta ...