Flink源码系列——获取StreamGraph的过程

本文详细分析了Flink中StreamGraph的生成过程,从flatMap、reduce到sink的转化,解释了如何将数据流的转换操作保存在transformations列表中,并通过StreamGraphGenerator的generate方法转化为StreamGraph。在转化过程中,涉及到节点和边的创建,包括SourceTransformation、OneInputTransformation和SinkTransformation。转化后,使用StreamGraphVisualizer可以将转换结果以图形形式展示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

接着《Flink源码系列——一个简单的数据处理功能的实现过程》一文的结尾继续分析,在完成对数据流的转换操作之后,需要执行任务,这时会调用如下代码:

env.execute("Socket Window WordCount");

在StreamExecutionEnvironment中,这是一个抽象方法,具体的实现在其子类中,其子类StreamContextEnvironment的实现如下:

public JobExecutionResult execute(String jobName) throws Exception {
   Preconditions.checkNotNull("Streaming Job name should not be null.");
   StreamGraph streamGraph = this.getStreamGraph();
   streamGraph.setJobName(jobName);
   transformations.clear();
   // execute the programs
   if (ctx instanceof DetachedEnvironment) {
      LOG.warn("Job was executed in detached mode, the results will be available on completion.");
      ((DetachedEnvironment) ctx).setDetachedPlan(streamGraph);
      return DetachedEnvironment.DetachedJobExecutionResult.INSTANCE;
   } else {
      return ctx
         .getClient()
         .run(streamGraph, ctx.getJars(), ctx.getClasspaths(), ctx.getUserCodeClassLoader(), ctx.getSavepointRestoreSettings())
         .getJobExecutionResult();
   }
}

这里先分析StreamGraph的获取过程,所以这里只看这句代码的执行过程:

StreamGraph streamGraph = this.getStreamGraph();

进一步的调用了父类的方法,实现如下:

public StreamGraph getStreamGraph() {
   if (transformations.size() <= 0) {
      throw new IllegalStateException("No operators defined in streaming topology. Cannot execute.");
   }
   return StreamGraphGenerator.generate(this, transformations);
}

这里的transformations这个列表就是在对数据流的处理过程中,会将flatMap、reduce这些转换操作对应的StreamTransformation保存下来的列表,根据对数据流做的转换操作,这个列表中,当前保存的对象有:

a、表示flatMap操作的OneInputTransformation对象,其input属性指向的是数据源的转换SourceTransformation。
b、表示reduce操作的OneInputTransformation对象,其input属性指向的是表示keyBy的转换PartitionTransformation,而PartitionTransformation的input属性指向的是flatMap的转换OneInputTransformation;
c、sink操作对应的SinkTransformation对象,其input属性指向的是reduce转化的OneInputTransformation对象。

接着调用StreamGraphGenerator的generate方法来获取transformations列表对应的StreamGraph。从这里可以看出,数据流在经过各种转换操作之后,各种转换的相关信息都已经保存在了transformations这个列表里的元素中了。继续向下看代码:

public static StreamGraph generate(StreamExecutionEnvironment env, List<StreamTransformation<?>> transformations) {
   return new StreamGraphGenerator(env).generateInternal(transformations);
}

可以看到,先以evn为构造函数的入参,构建了一个StreamGraphGenerator实例,看下构造函数的实现:

private StreamGraphGenerator(StreamExecutionEnvironment env) {
   this.streamGraph = new StreamGraph(env);
   this.streamGraph.setChaining(env.isChainingEnabled());
   this.streamGraph.setStateBackend(env.getStateBackend());
   this.env = env;
   this.alreadyTransformed = new HashMap<>();
}

这个构造函数是private的,所以StreamGraphGenerator的实例构造只能通过其静态的generate方法。另外在构造函数中,初始化了一个StreamGraph实例,并设置了一些属性值,然后给env赋值,并初始化alreadyTransformed为一个空map。再看下StreamGraph的构造函数实现。

public StreamGraph(StreamExecutionEnvironment environment) {
   this.environment = environment;
   this.executionConfig = environment.getConfig();
   this.checkpointConfig = environment.getCheckpointConfig();
   /** 构建一个空的新StreamGraph */
   clear();
}

public void clear() {
   streamNodes = new HashMap<>();
   virtualSelectNodes = new HashMap<>();
   virtualSideOutputNodes = new HashMap<>();
   virtualPartitionNodes = new HashMap<>();
   vertexIDtoBrokerID = new HashMap<>();
   vertexIDtoLoopTimeout  = new HashMap<>();
   iterationSourceSinkPairs = new HashSet<>();
   sources = new HashSet<>();
   sinks = new HashSet<>();
}

可以看出其构造函数中就是做了一些初始化的操作,给StreamGraph的各个属性设置初始值,都是一些空集合。
在获取到StreamGraphGenerator的实例后,继续看其generatorInternal方法的逻辑:

private StreamGraph generateInternal(List<StreamTransformation<?>> transformations) {
   for (StreamTransformation<?> transformation: transformations) {
      transform(transformation);
   }
   return streamGraph;
}

逻辑很明了,就是顺序遍历transformations列表中的元素,然后依次转换,最后返回转化好的StreamGraph的实例。


1、flatMap对应StreamTransformation子类实例的转化

接下来就看下,对于transformations列表中的每个元素是如何转化的。

private Collection<Integer> transform(StreamTransformation<?> transform) {
   /** 判断传入的transform是否已经被转化过, 如果已经转化过, 则直接返回转化后对应的结果 */
   if (alreadyTransformed.containsKey(transform)) {
      return alreadyTransformed.get(transform);
   }
   LOG.debug("Transforming " + transform);
   /** 对于该transform, 如果没有设置最大并行度, 则尝试获取job的最大并行度, 并设置给它 */
   if (transform.getMaxParallelism() <= 0) {
      /** 如果最大并行度没有设置, 如果job设置了最大并行度,则获取job最大并行度 */
      int globalMaxParallelismFromConfig = env.getConfig().getMaxParallelism();
      if (globalMaxParallelismFromConfig > 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值