Flink ジョブの送信 StreamGraph 構築のソース コード分析

目次

グラフ概要

1. ストリームコンピューティングアプリケーションにおけるグラフ変換

2. バッチ処理アプリケーションのグラフ変換

3.Table & SQL APIのグラフ変換

フローグラフ

1.ストリームノード

2.ストリームエッジ

3.StreamGraphによるソースコード解析の生成


グラフ概要

さまざまな Flink API とさまざまなレベルの Graph 間の対応関係を次の図に示します。

1. ストリームコンピューティングアプリケーションにおけるグラフ変換

ストリーム コンピューティング アプリケーションの場合、DataStream API 呼び出しは最初に Transformation に変換され、次にStreamGraph->JobGraph->ExecutionGraph の 3 層変換 (Flink の組み込みデータ構造) を経て、最後に Flink のスケジューリング実行を経て開始される必要があります。 Flink クラスター。コンピューティング タスクは、物理的に実行されるタスク間のトポロジ関係である物理実行グラフを形成します。ただし、ランタイムの概念である Flink には、対応するグラフ データ構造がありません。

2. バッチ処理アプリケーションのグラフ変換

バッチ処理アプリケーションの場合は、まず DataSet API を OptimizedPlan に変換し、次にそれを JobGraph に変換します。バッチ処理とストリームコンピューティングは JobGraph 上で統合されます。

3.Table & SQL APIのグラフ変換

テーブル&SQL APIは高度なAPIであり、開発時にはバッチ処理かストリームコンピューティングかを区別せず、基本的に両者の構文に違いはありません。現在、DataStream API はバッチ コンピューティング タスクとストリーム処理タスクを均一に作成できます。新しい Blink Table Planner と古い Flink Table Planner は、Table & SQL モジュールで使用されます。Flink Table Planner は将来的に徐々に廃止され、最終的には Flink から削除される予定です。

Blink Table Planner では、バッチ処理とストリーム コンピューティングの両方がストリーム コンピューティング システムに依存しているため、グラフ変換プロセスはバッチ処理アプリケーションでもストリーム コンピューティング アプリケーションでも同じです。旧 Flink Table Planner では、ストリーム コンピューティングは DataStream API に依存しており、そのグラフ変換プロセスはストリーム コンピューティング アプリケーションのグラフ変換プロセスでしたが、バッチ処理は DataSet API に依存しているため、その変換プロセスはバッチ処理のグラフ変換プロセスでした。アプリケーション。

将来的には、DataSet API は廃止され、最終的には Flink から削除されるため、グラフ変換プロセスは存在しなくなります。以下では、ストリーム コンピューティングの変換プロセスを例に説明します。

フローグラフ

上の図からわかるように、StreamGraph は StreamNode と StreamEdge で構成されます。

1.ストリームノード

StreamNode は StreamGraph のノードであり、Transformation を変換したものであり、演算子を表す StreamNode として理解できますが、論理的には StreamGraph 上の実体と仮想的な StreamNode を持ちます。StreamNode は複数の入力と複数の出力を持つことができます。

2.ストリームエッジ

StreamEdge は StreamGraph のエッジであり、2 つの StreamNode を接続するために使用されます。StreamNode は複数の送信エッジと受信エッジを持つことができます。StreamEdge には、バイパス出力、パーティショナー、フィールド フィルター出力 (SQL Select でフィールドを選択するのと同じロジック) およびその他の情報が含まれています。

3.StreamGraphによるソースコード解析の生成

StreamGraph を生成する入り口は StreamExecutionEnvironment にあります

  public JobExecutionResult execute(String jobName) throws Exception {
        Preconditions.checkNotNull(jobName, "Streaming Job name should not be null.");
​
        return execute(getStreamGraph(jobName));
    }

getStreamGraph() メソッドを入力します。

 @Internal
    public StreamGraph getStreamGraph(String jobName, boolean clearTransformations) {
        StreamGraph streamGraph = getStreamGraphGenerator().setJobName(jobName).generate();
        if (clearTransformations) {
            this.transformations.clear();
        }
        return streamGraph;
    }

StreamGraph は、SinkTransformation (出力) から SourceTransformation まで順方向にトレースして、StreamGraphGenerator で生成されます。走査プロセス中に走査しながら StreamGraph を構築する

generate()メソッドを入力します

public StreamGraph generate() {
        streamGraph = new StreamGraph(executionConfig, checkpointConfig, savepointRestoreSettings);
   ...
       //实际StreamGraph的生成
        for (Transformation<?> transformation : transformations) {
            transform(transformation);
        }
​
   ...
        final StreamGraph builtStreamGraph = streamGraph;
​
   ...
​
        return builtStreamGraph;
    }

トランスフォーム(変換)メソッドを入力する場合、トランスレーターが存在するかどうかに応じて、異なるトランスレートメソッドが入力されます。

     if (translator != null) {
            transformedIds = translate(translator, transform);
        } else {
            transformedIds = legacyTransform(transform);
        }

変換 (トランスレーター、変換) メソッドを入力し、バッチとストリーミングに応じてさまざまなメソッドを調整します。

 return shouldExecuteInBatchMode
        ? translator.translateForBatch(transform, context)
        : translator.translateForStreaming(transform, context);

「translateForStreaming(transform,context)」と入力し、さまざまな変換に応じてさまざまなtranslateForStreaming(transform,context)メソッドを入力します。

ここでは OneInputTransformation.java を分析し、最後に AbstractOneInputTransformationTranslator の translationInternal() を呼び出してStreamGraph を構築します。

  • まず演算子を StreamGraph に追加します

streamGraph.addOperator(
                transformationId,
                slotSharingGroup,
                transformation.getCoLocationGroupKey(),
                operatorFactory,
                inputType,
                transformation.getOutputType(),
                transformation.getName());
  • SetStateKeySelector

if (stateKeySelector != null) {
            TypeSerializer<?> keySerializer = stateKeyType.createSerializer(executionConfig);
            streamGraph.setOneInputStateKey(transformationId, stateKeySelector, keySerializer);
        }
  • 並列処理数と最大並列処理数の設定
 streamGraph.setParallelism(transformationId, parallelism);
        streamGraph.setMaxParallelism(transformationId, transformation.getMaxParallelism());
  • StreamEdge のエッジを構築し、上流と下流の StreamNode を関連付けます

for (Integer inputId : context.getStreamNodeIds(parentTransformations.get(0))) {
            streamGraph.addEdge(inputId, transformationId, 0);
        }

PartitionTransformationTranslator.java の translationInternal() メソッド内

 private Collection<Integer> translateInternal(
            final PartitionTransformation<OUT> transformation, final Context context) {
      ...
​
        final StreamGraph streamGraph = context.getStreamGraph();
​
​
      ...
        List<Integer> resultIds = new ArrayList<>();
​
        for (Integer inputId : context.getStreamNodeIds(input)) {
            final int virtualId = Transformation.getNewNodeId();
            //todo 添加一个虚拟分区节点,不会生成StreamNode
            streamGraph.addVirtualPartitionNode(
                    inputId,
                    virtualId,
                    transformation.getPartitioner(),
                    transformation.getShuffleMode());
            resultIds.add(virtualId);
        }
        return resultIds;
    }

上記のコードからわかるように、PartitionTransformation のキャプチャと置換では特定の StreamNode と StreamEdge が生成されませんが、streamGraph.addVirtualPartitionNode() メソッドを通じて仮想ノードが追加されます。データ パーティションのダウンストリーム Tramsformation が StreamEdge を追加すると (streamGraph.addEdge() を呼び出す)、次のコードに示すように、Partitioner パーティショナーは StreamEdge にカプセル化されます。

 private void addEdgeInternal(
            Integer upStreamVertexID,
            Integer downStreamVertexID,
            int typeNumber,
            StreamPartitioner<?> partitioner,
            List<String> outputNames,
            OutputTag outputTag,
            ShuffleMode shuffleMode) {
        //todo 如果上游是sideOutput时,递归调用,并传入sideOutput 信息
        if (virtualSideOutputNodes.containsKey(upStreamVertexID)) {
            int virtualId = upStreamVertexID;
            upStreamVertexID = virtualSideOutputNodes.get(virtualId).f0;
            if (outputTag == null) {
                outputTag = virtualSideOutputNodes.get(virtualId).f1;
            }
            addEdgeInternal(
                    upStreamVertexID,
                    downStreamVertexID,
                    typeNumber,
                    partitioner,
                    null,
                    outputTag,
                    shuffleMode);
         //todo 如果上游是Partition时,递归调用,并传入Partition信息
        } else if (virtualPartitionNodes.containsKey(upStreamVertexID)) {
            int virtualId = upStreamVertexID;
            upStreamVertexID = virtualPartitionNodes.get(virtualId).f0;
            if (partitioner == null) {
                partitioner = virtualPartitionNodes.get(virtualId).f1;
            }
            shuffleMode = virtualPartitionNodes.get(virtualId).f2;
            addEdgeInternal(
                    upStreamVertexID,
                    downStreamVertexID,
                    typeNumber,
                    partitioner,
                    outputNames,
                    outputTag,
                    shuffleMode);
        } else {
            //todo 不是以上逻辑,构建真正的StreamEdge
            StreamNode upstreamNode = getStreamNode(upStreamVertexID);
            StreamNode downstreamNode = getStreamNode(downStreamVertexID);
            //todo 没有指定Partition时,会为其选择forwa或者rebalance分区
            if (partitioner == null
                    && upstreamNode.getParallelism() == downstreamNode.getParallelism()) {
                partitioner = new ForwardPartitioner<Object>();
            } else if (partitioner == null) {
                partitioner = new RebalancePartitioner<Object>();
            }
​
          ...
​
            if (shuffleMode == null) {
                shuffleMode = ShuffleMode.UNDEFINED;
            }
​
            //todo  创建StreamEdge,并将该StreamEdge
            StreamEdge edge =
                    new StreamEdge(
                            upstreamNode,
                            downstreamNode,
                            typeNumber,
                            partitioner,
                            outputTag,
                            shuffleMode);
            //todo 创建StreamEdge,并将StreamEdge添加到上游的输出、下游的输入
            getStreamNode(edge.getSourceId()).addOutEdge(edge);
            getStreamNode(edge.getTargetId()).addInEdge(edge);
        }
    }
​

公開アカウントをフォローして、Flink について詳しく学びましょう

おすすめ

転載: blog.csdn.net/qq_24186017/article/details/127001832