Flink 分流
所谓“分流”,就是将一条数据流拆分成完全独立的两条、甚至多条流。也就是基于一个DataStream,得到完全平等的多个子DataStream,如下图所示。一般来说,会定义一些筛选条件,将符合条件的数据拣选出来放到对应的流里。
处理函数本身可以认为是一个转换算子,它的输出类型是单一的,处理之后得到的仍然是一个DataStream;而侧输出流则不受限制,可以任意自定义输出数据,它们就像从“主流”上分叉出的“支流”。尽管看起来主流和支流有所区别,不过实际上它们都是某种类型的DataStream,所以本质上还是平等的。利用侧输出流就可以很方便地实现分流操作,而且得到的多条DataStream类型可以不同,这就给我们的应用带来了极大的便利。关于处理函数中侧输出流的用法,我们已经在7.5节做了详细介绍。简单来说,只需要调用上下文ctx的.output()方法,就可以输出任意类型的数据了。而侧输出流的标记和提取,都离不开一个“输出标签”(OutputTag),它就相当于split()分流时的“戳”,指定了侧输出流的id和类型。
侧输出流实现分流
/** * 侧输出流实现分流 */ public class SplitSDtreamTest { public static void main(String[] args) throws Exception { StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); env.setParallelism(1); SingleOutputStreamOperator<Event> eventStream = env.addSource(new ClickSource()) .assignTimestampsAndWatermarks(WatermarkStrategy. <Event>forBoundedOutOfOrderness(Duration.ofSeconds(2)) .withTimestampAssigner(new SerializableTimestampAssigner<Event>() { @Override public long extractTimestamp(Event element, long recordTimestamp) { return element.timestamp * 10000; } })); //定义侧输出流标签 OutputTag<Tuple3<String, String, Long>> yilin = new OutputTag<Tuple3<String, String, Long>>("依琳"){}; OutputTag<Tuple3<String, String, Long>> linghc = new OutputTag<Tuple3<String, String, Long>>("令狐冲"){}; SingleOutputStreamOperator<Event> processStream = eventStream.process(new ProcessFunction<Event, Event>() { @Override public void processElement(Event value, Context ctx, Collector<Event> out) throws Exception { if (value.user.equals("依琳")) { ctx.output(yilin, Tuple3.of(value.user, value.url, value.timestamp)); } else if (value.user.equals("令狐冲")) { ctx.output(linghc, Tuple3.of(value.user, value.url, value.timestamp)); } else { out.collect(value); } } }); //获取主流 processStream.print(" else "); //获取侧输出流 processStream.getSideOutput(yilin).print(); processStream.getSideOutput(linghc).print(); env.execute(); } }
这里定义了两个侧输出流,分别拣选Mary的浏览事件和Bob的浏览事件;由于类型已经确定,可以只保留(用户id, url, 时间戳)这样一个三元组。而剩余的事件则直接输出到主流,类型依然保留Event,就相当于之前的elseStream。这样的实现方式显然更简洁,也更加灵活。