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结构时才会理所当然。