文章目录
JobGraph是所有类型的作业与集群运行时之间的通信协议,是作业提交使用的统一数据结构。不管是流还是批,最终都得转换成Flink集群能够接受的JobGraph。之所以要搞这么多的图转换,就是为了更好的兼容更多类型的作业,让不同类型的作业都能运行在同一套集群Runtime中。
StreamGraph借助StreamingJobGraphGenerator转换成JobGraph的核心逻辑如下:
/**
* 将StreamGraph转换为JobGraph的主要逻辑
*/
private JobGraph createJobGraph() {
// 对StreamGraph进行“预检查”,例如在开启Checkpoint时,StreamGraph每个节点的StreamOperator是否实现了InputSelectable接口
preValidate();
// 用StreamGraph的调度模式,作为JobGraph的调度模式
jobGraph.setScheduleMode(streamGraph.getScheduleMode());
/**
* 广度优先遍历StreamGraph:将StreamGraph中的所有Source排序后,从Source开始为StreamGraph中的每个StreamNode生成hash值(根据StreamNode ID),用作JobVertex ID。
* 精髓:基于Queue实现,从Source节点出发,先遍历它的下游,再遍历下游的下游
*/
Map<Integer, byte[]> hashes = defaultStreamGraphHasher.traverseStreamGraphAndGenerateHashes(streamGraph);
// 生成旧版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<>();
/**
* 从Source节点开始递归创建JobVertex节点:这里会将多个“符合链化条件”的StreamNode链化在一起,HeadOperator作为JobVertex节点。
* 链化形成OperatorChain,就是要减少数据在TaskManager之间因为网络传输而造成的性能损耗
*/
setChaining(hashes, legacyHashes, chainedOperatorHashes);
/**
* 将每个JobVertex节点的所有输入边,序列化到这个JobVertex节点的StreamConfig中
*/
setPhysicalEdges();
// 设置Slot共享组和CoLocation策略
setSlotSharingAndCoLocation();
// 设置JobGraph的管理内存比例
setManagedMemoryFraction(
Collections.unmodifiableMap(jobVertices),
Collections.unmodifiableMap(vertexConfigs),
Collections.unmodifiableMap(chainedConfigs),
id -> streamGraph.getStreamNode(id).getMinResources(),
id -> streamGraph.getStreamNode(id).getManagedMemoryWeight());
// 配置checkpoint
configureCheckpointing();
// 配置savepoint
jobGraph.setSavepointRestoreSettings(streamGraph.getSavepointRestoreSettings());
// Job中用到的自定义文件
JobGraphGenerator.addUserArtifactEntries(streamGraph.getUserArtifacts(), jobGraph);
try {
// 最后,将StreamGraph中的执行参数ExecutionConfig,交给JobGraph
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");
}
return jobGraph;
}
1. StreamNode哈希化
在正式构建JobGraph之前,会对StreamGraph中的全部StreamNode(根据StreamNode ID)进行哈希化处理,生成的hash值将来会被当做JobVertex ID。
/**
* (从Source节点开始)为StreamGraph中的每个StreamNode节点(根据StreamNode ID)生成一个hash值,作为JobVertex节点的ID
*/
@Override
public Map<Integer, byte[]> traverseStreamGraphAndGenerateHashes(StreamGraph streamGraph) {
final HashFunction hashFunction = Hashing.murmur3_128(0);
final Map<Integer, byte[]> hashes = new HashMap<>();
Set<Integer> visited = new HashSet<>();
Queue<StreamNode> remaining = new ArrayDeque<>();
// 保存StreamGraph中的所有Source StreamNode
List<Integer> sources = new ArrayList<>();
/**取出StreamGraph中所有的Source节点,保存到List中*/
for (Integer sourceNodeId : streamGraph.getSourceIDs()) {
sources.add(sourceNodeId);
}
// 排序
Collections.sort(sources);
/**遍历StreamGraph中的Source节点,将其保存到Queue和Set集合中*/
for (Integer sourceNodeId : sources) {
// 把Source节点添加到Queue中
remaining.add(streamGraph.getStreamNode(sourceNodeId));
// 将Source节点添加到Set集合中
visited.add(sourceNodeId);
}
StreamNode currentNode;
// 从Queue的队头拉取StreamNode并remove(从Source节点开始),对其进行哈希化处理。
while ((currentNode = remaining.poll()) != null) {
// 如果对StreamNode进行哈希化成功,会返回true
if (generateNodeHash(currentNode, hashFunction, hashes, streamGraph.isChainingEnabled(), streamGraph)) {
/**遍历当前StreamNode的所有输出边,让它的下游StreamNode也参与到哈希化处理的过程中*/
for (StreamEdge outEdge : currentNode.getOutEdges()) {
// 当前StreamNode的“下游StreamNode”
StreamNode child = streamGraph.getTargetVertex(outEdge);
/**
* 如果Set中没有当前StreamNode的下游StreamNode,就将其添加到Queue和Set中。
* 这样一来,下轮while循环时就会将其poll出来,并继续哈希化。如此循环,所有StreamNode都会被哈希化。
*/
if (!visited.contains(child.getId())) {
remaining.add(child);
visited.add(child.getId());
}
}
} else {
visited.remove(currentNode.getId());
}
}
return hashes;
}
(基于广度优先遍历)将StreamGraph中的所有Source StreamNode保存到Queue中,并从Source开始,沿着StreamEdge向下,逐个对下游的StreamNode进行哈希化。最终,StreamGraph中的所有StreamNode全都完成哈希化,且以“StreamNode ID:hash值”的映射关系,保存在Map集合中。
同时为了向后兼容,还会额外为每个StreamNode生成旧版hash值:
/**
* 手动为StreamNode生产对应的hash值,将来会被当做JobVertex ID
*/
@Override
public Map<Integer, byte[]> traverseStreamGraphAndGenerateHashes(StreamGraph streamGraph) {
HashMap<Integer, byte[]> hashResult = new HashMap<>();
// 遍历StreamGraph内的每个StreamNode,为其生成对应的hash值
for (StreamNode streamNode