构建Transformation树

文章详细阐述了ApacheFlink中Transformation的概念,如何通过map转换操作创建StreamMap,以及Transformation树的构建过程。转化操作如map会生成对应的StreamOperator,并通过Transformation树形成StreamGraph,用于后续的执行计划生成。此外,还提到了物理Transformation和虚拟Transformation的区别。

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

Transformation表示从一个DataStream生成另一个DataStream的转换操作,就好像一张纸经过一番折叠操作就会成为一个纸飞机,而这个“一番折叠操作”正是Transformation。DataStream提供了一系列转换方法,分别有各自对应的StreamOperator,自然也就有对应的xxxTransformation。伴随着调用DataStream的一系列转换方法,逐渐形成了一棵“Transformation树”(包含:物理Transformation和虚拟Transformation)。正是基于这棵“Transformation树”,才能够生成StreamGraph结构。

以map转换操作为例,要求提供对应的MapFunction,它会被用来创建对应的StreamOperator,即StreamMap。

/**
 * 调用DataStream提供的map转换方法
 */
public <R> SingleOutputStreamOperator<R> map(MapFunction<T, R> mapper) {

    // 根据map转换对应的MapFunction,获取本次map转换的输出类型。其中getType()方法会获取到上一个Transformation的输出类型,即本次Transformation的输入类型
    TypeInformation<R> outType = TypeExtractor.getMapReturnTypes(clean(mapper), getType(),
                                                                 Utils.getCallLocationName(), true);

    // 根据本次转换的MapFunction函数和输出类型,构建本次map转换对应的xxxTransformation
    return map(mapper, outType);
}

/**
 * map转换方法:创建出来的StreamOperator为StreamMap,并将转换逻辑(也就是xxxFunction)交给StreamOperator持有
 */
public <R> SingleOutputStreamOperator<R> map(MapFunction<T, R> mapper, TypeInformation<R> outputType) {
    // 参数:算子name,输出类型,StreamOperator(基于自定义Function构建)
    return transform("Map", outputType, new StreamMap<>(clean(mapper)));
}

而MapFunction也会以成员变量持有的方式,保存在父类AbstractUdfStreamOperator中。

接下来会利用本次map转换所对应的上一次Transformation(DataStream通过成员变量保留)和本次转换所对应的StreamOperator等信息,构建本次转换所对应的Transformation。


public <R> SingleOutputStreamOperator<R> transform(
    String operatorName, // 算子name
    TypeInformation<R> outTypeInfo, // 本次转换的输出类型
    // StreamMap算子继承AbstractUdfStreamOperator,实现OneInputStreamOperator接口
    OneInputStreamOperator<T, R> operator) {

    // SimpleOperatorFactory.of()方法:根据StreamOperator的类型,创建SimpleOperatorFactory,并将刚刚创建好的StreamOperator交由OperatorFactory持有
    return doTransform(operatorName, outTypeInfo, SimpleOperatorFactory.of(operator));
}

/**
 * 调用物理转换方法,会生成对应的StreamOperator和物理的xxxTransformation,
 * 并且xxxTransformation会被添加到StreamExecutionEnvironment的List中,形成Transformation树。后面会用它来构建StreamGraph
 */
protected <R> SingleOutputStreamOperator<R> doTransform(
    String operatorName,
    TypeInformation<R> outTypeInfo,
    StreamOperatorFactory<R> operatorFactory) {

    // read the output type of the input Transform to coax out errors about MissingTypeInfo
    // 保险起见,确保不会出现InvalidTypesException,保证本次的转换操作不会出问题
    transformation.getOutputType();

    // 创建本次物理转换所对应的xxxTransformation实例
    // 物理的xxxTransformation会通过“成员变量持有”的方式,(通过StreamOperatorFactory间接)持有StreamOperator
    OneInputTransformation<T, R> resultTransform = new OneInputTransformation<>(
        this.transformation, // 上一次的Transformation转换操作
        operatorName, // 当前算子的name
        operatorFactory, // MapFunction-->StreamMap(StreamOperator) -->StreamOperatorFactory
        outTypeInfo, // 当前算子(进行本次转换)对应的输出类型
        // 当前算子的并行度,默认为env整体的并行度
        environment.getParallelism());

    // SingleOutputStreamOperator:每次转换操作完毕后,返回给开发者继续操作的数据结构
    @SuppressWarnings({"unchecked", "rawtypes"})
    SingleOutputStreamOperator<R> returnStream = new SingleOutputStreamOperator(environment, resultTransform);

    // 将本次转换生成的这个xxxTransformation添加到List<Transformation>列表中,它会被用来生成StreamGraph
    getExecutionEnvironment().addOperator(resultTransform);

    return returnStream;
}

接下来就是将本次转换操作对应的Transformation添加到StreamExecutionEnvironment的Transformation集合中,用于构建Transformation树。

// DataStream之间的转换操作,都会生成Transformation对象。Transformation是DataStream的根源。
protected final List<Transformation<?>> transformations = new ArrayList<>();


@Internal
public void addOperator(Transformation<?> transformation) {
    Preconditions.checkNotNull(transformation, "transformation must not be null.");
    // 将生成的Transformation保存到List<Transformation>中
    this.transformations.add(transformation);
}

通过成员变量持有的方式,Transformation持有了StreamOperator、StreamOperator持有了自定义Function。

对于物理Transformation,伴随着物理转换操作,会生成对应的StreamOperator(算子)和物理的Transformation,并且物理Transformation会被添加到StreamExecutionEnvironment的Transformation集合中,以便后面用它来构建StreamGraph;而对于“虚拟Transformation”:伴随着虚拟转换操作,只会生成“虚拟Transformation”。后面对“虚拟Transformation”进行解析时,会添加虚拟节点,其本质操作就是按照映射关系:“虚拟节点id:Tuple3<>(Transformation ID,Partitioner,ShuffleMode)”,将其保存到Map结构中。

/**
 * shuffle操作
 */
public DataStream<T> shuffle() {
    return setConnectionType(new ShufflePartitioner<T>());
}

/**
 * 生成“虚拟Transformation”
 */ 
protected DataStream<T> setConnectionType(StreamPartitioner<T> partitioner) {
    // 生成“虚拟Transformation”,这个“虚拟Transformation”并不会被添加到StreamExecutionEnvironment的List中
    return new DataStream<>(this.getExecutionEnvironment(), new PartitionTransformation<>(this.getTransformation(), partitioner));
}

注意,在创建一个Transformation时,会以成员变量“持有”的方式,通过input指针将本次转换的上一次转换所对应的Transformation记录下来,形成关联关系。也就是:后Transformation —> 前Transformation

OneInputTransformation<T, R> resultTransform = new OneInputTransformation<>(
    this.transformation, // 上一次的Transformation转换操作
    operatorName, // 当前算子的name
    operatorFactory, // MapFunction-->StreamMap(StreamOperator) -->StreamOperatorFactory
    outTypeInfo, // 当前算子(进行本次转换)对应的输出类型
    // 当前算子的并行度,默认为env整体的并行度
    environment.getParallelism());

在这里插入图片描述

前、后Transformation有了关联关系,在构建StreamGraph结构时才会理所当然。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值