Flink之StreamGraph生成源码分析

Flink之StreamGraph生成源码分析

一、StreamGraph是什么?

是用户提交的代码,直接生产的数据流图

二、生成StreamGraph的过程

2.1 触发执行生成StreamGraph

程序执行即env.execute("Java WordCount")这行代码,这里以RemoteStreamEnvironment环境为例

@Override
public JobExecutionResult execute(String jobName) throws ProgramInvocationException {
    StreamGraph streamGraph = getStreamGraph();
    streamGraph.setJobName(jobName);
    transformations.clear();
    return executeRemotely(streamGraph, jarFiles);
}

StreamGraph streamGraph = getStreamGraph();将生成StreamGraph,进入该方法发现

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

StreamGraph是由StreamGraphGenerator.generate(this, transformations)实现,进而是该类的generateInternal方法实现

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

最终根据transform(transformation);生成StreamGraph

Collection<Integer> transformedIds;
if (transform instanceof OneInputTransformation<?, ?>) {
    transformedIds = transformOneInputTransform((OneInputTransformation<?, ?>) transform);
} else if (transform instanceof TwoInputTransformation<?, ?, ?>) {
    transformedIds = transformTwoInputTransform((TwoInputTransformation<?, ?, ?>) transform);
} else if (transform instanceof SourceTransformation<?>) {
    transformedIds = transformSource((SourceTransformation<?>) transform);
} 
     ...
else {
    throw new IllegalStateException("Unknown transformation: " + transform);
}
     ...
if (transform.getUserProvidedNodeHash() != null) {
    streamGraph.setTransformationUserHash(transform.getId(), transform.getUserProvidedNodeHash());
}

if (transform.getMinResources() != null && transform.getPreferredResources() != null) {
    streamGraph.setResources(transform.getId(), transform.getMinResources(), transform.getPreferredResources());
}

2.2 transformation集合的初始化

我们看到transform(transformation);生成StreamGraph的参数是transformation,那么该值是什么?在何时赋值的呢?通过追溯代码StreamGraphGenerator.generate(this, transformations)来自环境类,是该属性protected final List<StreamTransformation<?>> transformations = new ArrayList<>();现在研究下该属性怎么赋值的,在我们的各个算子的代码中,例如env.addSource().map().filter().sink(),这里已filter为例,在DataStream类型中

public SingleOutputStreamOperator<T> filter(FilterFunction<T> filter) {
		return transform("Filter", getType(), new StreamFilter<>(clean(filter)));

	}

在该方法的transformgetExecutionEnvironment().addOperator(resultTransform);

public <R> SingleOutputStreamOperator<R> transform(String operatorName, TypeInformation<R> outTypeInfo, OneInputStreamOperator<T, R> operator) {
    OneInputTransformation<T, R> resultTransform = new OneInputTransformation<>(
            this.transformation,
            operatorName,
            operator,
            outTypeInfo,
            environment.getParallelism());
            ...
    getExecutionEnvironment().addOperator(resultTransform);

    return returnStream;
}

把算子添加到transformations完成赋值

2.3 怎么构建StreamGraph

也就是梳理StreamGraphGenerator.transform()的过程。这里以transformOneInputTransform(OneInputTransformation<IN, OUT> transform)为例
首先Collection<Integer> inputIds = transform(transform.getInput());递归对该算子的直接上游算子转换,获取直接上游的id集合,递归结束的条件是alreadyTransformed.containsKey(transform)
再次:String slotSharingGroup = determineSlotSharingGroup(transform.getSlotSharingGroup(), inputIds);分配共享算子slot
然后在StreamGraph构建节点,最终加入节点集合。

streamGraph.addOperator(transform.getId(), slotSharingGroup, transform.getCoLocationGroupKey(), transform.getOperator(), transform.getInputType(), transform.getOutputType(), transform.getName());

最后在StreamGraph构建边

for (Integer inputId: inputIds) {
    streamGraph.addEdge(inputId, transform.getId(), 0);
}

然后调用addEdgeInternal方法,无论是虚拟节点还是分区节点,都会执行

StreamEdge edge = new StreamEdge(upstreamNode, downstreamNode, typeNumber, outputNames, partitioner, outputTag);
getStreamNode(edge.getSourceId()).addOutEdge(edge);
getStreamNode(edge.getTargetId()).addInEdge(edge);

注意:

  • edge.getSourceId()就是new StreamEdge的upstreamNode的id
  • edge.getTargetId()就是new StreamEdge的downstreamNode的id

StreamEdge通过sourceId 和targetId属性可以获取边的两个节点。

  • addOutEdge是获取节点的输出边集合
  • addInEdge(edge)是获取节点的输入边集合。

就是找到该边属于哪个节点的输入边,属于哪个节点的输出边。
StreamNode通过inEdges和outEdges可以获取连接节点的边集合

  • 另外downstreamNode是本节点,upstreamNode是本节点的上游节点。

通过节点可以知道节点的边,通过边可以找到边的两个节点,所以就以完整的构建成StreamGraph图。

可以通过env.getExecutionPlan()获取StreamGraph的字符串表示形式。

三、涉及几个关键类说明

3.1 StreamTransformation

是所有算子的转换类的接口,每一个DataStream都有一个与之对应的StreamTransformation。它是逻辑上的概念和运行时无关。

3.1.1 主要属性
  • name:转换器名字
  • uid:用户指定uid
  • outputType:输出类型
  • slotSharingGroup:设置slot共享组,虚拟节点没有该值
3.1.2 核心抽象方法
  • setChainingStrategy:设置算子链策略
  • getTransitivePredecessors:返回前置算子,如果没有特别说明,getTransitivePredecessors的实现逻辑都是,由自身加input(上游StreamTransformation)组成的集合
3.1.3 实现的子类
  • SourceTransformation:表示source,不做任何的转换操作,是项目的根,无input属性
  • SinkTransformation:表示sink操作算子
  • OneInputTransformation:表示一个输入流的算子
  • TwoInputTransformation:表示两个输入流的算子
  • SplitTransformation:分拆流算子
  • SelectTransformation:选择算子
  • UnionTransformation:合并算子
  • PartitionTransformation:分区流算子
  • FeedbackTransformation
  • CoFeedbackTransformation

他们都有个input属性,是他们直接上游的算子

3.2 StreamGraph

StreamGraph是用户代码直接生成拓扑结果的图表示,表示各个算子的组合方式,是一个DAG图。

3.2.1 主要属性
  • Map<Integer, StreamNode> streamNodes:节点信息
  • Set sources:数据源节点集合
  • Set sinks:下沉节点集合
  • Map<Integer, Tuple2<Integer, List>> virtualSelectNodes:虚拟select节点集合
  • Map<Integer, Tuple2<Integer, OutputTag>> virtualSideOutputNodes:虚拟sideoutput节点集合
  • Map<Integer, Tuple2<Integer, StreamPartitioner<?>>> virtualPartitionNodes:分区节点集合

虚拟节点不会生成真正的节点,会连接上游的非虚拟节点

3.2.2 核心方法
  • addOperator:构建streamNodes集合
  • addEdge:构建边
  • addEdgeInternal:构建边,在该方法中,决定分区的策略,如果没有指定分区则按照上游和下游算子的并行度是否相同决定是本地分发,还是均匀分发
  • getJobGraph:生成JobGraph
  • getStreamingPlanAsJSON:StreamGraph字符串表示形式
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值