深入理解Flink基于StreamGraph构建JobGraph的流程


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 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值