接《Flink源码系列——获取StreamGraph的过程》获取到StreamGraph后,继续分析,如果通过获取到的StreamGraph来转化为JobGraph。转化逻辑在StreamingJobGraphGenerator这个类中,入口是createJobGraph(StreamGraph)方法。先是初始化了一个StreamingJobGraphGenerator的实例,StreamingJobGraphGenerator构造函数是私有的,只能通过这里进行实例构造,构造函数中就是做了一些基本的初始化的工作,并初始化了一个JobGraph实例,然后调用内部的私有方法createJobGraph()。
public static JobGraph createJobGraph(StreamGraph streamGraph) {
return new StreamingJobGraphGenerator(streamGraph).createJobGraph();
}
createJobGraph()方法就是jobGraph进行配置的主要逻辑,如下:
private JobGraph createJobGraph() {
/** 设置调度模式,采用的EAGER模式,既所有节点都是立即启动的 */
jobGraph.setScheduleMode(ScheduleMode.EAGER);
/** 第一步 */
/**
* 1.1
* 广度优先遍历StreamGraph,并且为每个SteamNode生成散列值, 这里的散列值产生算法,可以保证如果提交的拓扑没有改变,则每次生成的散列值都是一样的。
* 一个StreamNode的ID对应一个散列值。
*/
Map<Integer, byte[]> hashes = defaultStreamGraphHasher.traverseStreamGraphAndGenerateHashes(streamGraph);
/**
* 1.2
* 为向后兼容性生成遗留版本散列
* 目前好像就是根据用户对每个StreamNode设置的hash值,生产StreamNode对应的hash值
*/
List<Map<Integer, byte[]>> legacyHashes = new ArrayList<>(legacyStreamGraphHashers.size());
for (StreamGraphHasher hasher : legacyStreamGraphHashers) {
legacyHashes.add(hasher.traverseStreamGraphAndGenerateHashes(streamGraph));
}
/** 相连接的操作符的散列值对 */
Map<Integer, List<Tuple2<byte[], byte[]>>> chainedOperatorHashes = new HashMap<>();
/**
* 第二步
* 最重要的函数,生成JobVertex,JobEdge等,并尽可能地将多个节点chain在一起
*/
setChaining(hashes, legacyHashes, chainedOperatorHashes);
/**
* 第三步
* 将每个JobVertex的入边集合也序列化到该JobVertex的StreamConfig中
* (出边集合已经在setChaining的时候写入了)
*/
setPhysicalEdges();
/**
* 第四步
* 据group name,为每个 JobVertex 指定所属的 SlotSharingGroup,
* 以及针对 Iteration的头尾设置 CoLocationGroup
*/
setSlotSharing();
/**
* 第五步
* 配置checkpoint
*/
configureCheckpointing();
/** 将缓存文件的配置添加到configuration中 */
for (Tuple2<String, DistributedCache.DistributedCacheEntry> e : streamGraph.getEnvironment().getCachedFiles()) {
DistributedCache.writeFileInfoToConfig(e.f0, e.f1, jobGraph.getJobConfiguration());
}
/** 设置ExecutionConfig */
try {
jobGraph.setExecutionConfig(streamGraph.getExecutionConfig());
}
catch (IOException e) {
throw new IllegalConfigurationException("Could not serialize the ExecutionConfig." +
"This indicates that non-serializable types (like custom serializers) were registered");
}
/** 返回转化好的jobGraph */
return jobGraph;
}
jobGraph的整个产生过程就如上所示,接下来针对其中的主要步骤进行简单分析。
第一步、为每个节点产生散列值
这里就是根据StreamGraph的配置,给StreamGraph中的每个StreamNode产生一个长度为16的字节数组的散列值,这个散列值是用来后续生成JobGraph中对应的JobVertex的ID。在Flink中,任务存在从checkpoint中进行状态恢复的场景,而在恢复时,是以JobVertexID为依据的,所有就需要任务在重启的过程中,对于相同的任务,其各JobVertexID能够保持不变,而StreamGraph中各个StreamNode的ID,就是其包含的StreamTransformation的ID,而StreamTransformation的ID是在对数据流中的数据进行转换的过程中,通过一个静态的累加器生成的,比如有多个数据源时,每个数据源添加的顺序不一致,则有可能导致相同数据处理逻辑的任务,就会对应于不同的ID,所以为了得到确定的ID,在进行JobVertexID的产生时,需要以一种确定的方式来确定其值,要么是通过用户为每个ID直接指定对应的一个散列值,要么参考StreamGraph中的一些特征,为每个JobVert