Side Output简单来说就是在你程序执行过程中,你需要将从主流stream中获取额外的流的方式,也就是在处理一个数据流的时候,将这个流中的不同的业务类型或者不同条件的数据分别输出到不同的地方。
如果采用filter算子对数据做筛选,也可以满足这种需求,但是这样会造成数据流被复制多分,造成不必要的性能浪费。
Side Output在拆分数据流时,然后从每个流过滤出你不想拥有的数据,非常有用。
摘自 : Flink的处理机制以及侧输出应用
通常我们说sideoutput的使用场景如下:
举个例子来说明:
比如现在有一篇文章吧,单词长度不一,但是我们想对单词长度小于5的单词进行wordcount操作,同时又想记录下来哪些单词的长度大于了5,那么我们该如何做呢?
普遍的做法是:
datastream.filter(word.length>=5); //获取不统计的单词,也即是单词长度大于等于5。
datastream.filter(word.length <5);// 获取需要进行wordcount的单词。
这样数据,然后每次筛选都要保留整个流,然后遍历整个流,显然很浪费性能,假如能够在一个流了多次输出就好了,flink的侧输出提供了这个功能,侧输出的输出(sideoutput)类型可以与主流不同,可以有多个侧输出(sideoutput),每个侧输出不同的类型。
如果需要使用Side Output 则需要首先定义一个OutputTag,用这个OutputTag来标识,将流进行拆分。
// this needs to be an anonymous inner class, so that we can analyze the type
OutputTag outputTag = new OutputTag("side-output") {};
val outputTag = OutputTag[String]("side-output")
注意:OutputTag的包含的类型是根据需要进行侧输出的流的元素的类型确定的。
官方说明,产生的sideoutput的方法主要包含以下:
并且通过context 参数将拆分出来的元素标记上OutputTag 并发送到侧输出流中。
OutputTag stringOutputTag = new OutputTag("gt5word"){};
SingleOutputStreamOperator> processedStream = stringDataStreamSource.process(new ProcessFunction>() {
@Override
public void processElement(String value, Context ctx, Collector> out) throws Exception {
String[] splits = value.split("\\W+");
for (String word : splits) {
if (word.length() > 5) {
ctx.output(stringOutputTag, word);
} else {
out.collect(new Tuple2<>(word, 1));
}
}
}
});
注意:一定要在申明outputTag的时候加入最后的{},否则报错
val outputTag = OutputTag[String]("side-output-tag")
val processStream = input.process(new ProcessFunction[String, (String, Int)] {
override def processElement(value: String, ctx: ProcessFunction[String, (String, Int)]#Context, out: Collector[(String, Int)]): Unit = {
if (value.length > 5) {
ctx.output(outputTag, value)
} else {
out.collect((value, 1))
}
}
})
通过调用getSideOutput()方法可以获取相应的SideOutput数据流
SingleOutputStreamOperator sideOutputStream = processedStream.getSideOutput(stringOutputTag).map(new MapFunction() {
@Override
public String map(String value) throws Exception {
return "side out put stream is :" + value;
}
});
val sideOutputStream = processStream.getSideOutput(outputTag).map("side output is : " + _)
获取之后可以做相应的逻辑处理。
如果是处理延迟数据,则在窗口后使用
.sideOutputLateData(lateOutputTag)
即可给对应的数据进行打标。