Flink 多流转换 (二)合流

既然一条流可以分开,自然多条流就可以合并。在实际应用中,我们经常会遇到来源不同的多条流,需要将它们的数据进行联合处理。所以 Flink 中合流的操作会更加普遍,对应的API 也更加丰富。

联合(Union)

最简单的合流操作,就是直接将多条流合在一起,叫作流的“联合”(union),联合操作要求必须流中的数据类型必须相同,合并之后的新流会包括所有流中的元素,数据类型不变。这种合流方式非常简单粗暴,就像公路上多个车道汇在一起一样。

在这里插入图片描述
在代码中,我们只要基于 DataStream 直接调用.union()方法,传入其他 DataStream 作为参数,就可以实现流的联合了;得到的依然是一个DataStream:

stream1.union(stream2, stream3, ...)

注意:union()的参数可以是多个 DataStream,所以联合操作可以实现多条流的合并。

代码如下:Gitee案例

public class UnionTest {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);

        DataStreamSource<String> streamSource1 = env.socketTextStream("hadoop102", 8888);
        DataStreamSource<String> streamSource2 = env.socketTextStream("hadoop103", 8888);

        DataStream<Event> stream1 = streamSource1.map(data -> {
            String[] fields = data.split(" ");
            return new Event(fields[0].trim(), fields[1].trim(), Long.valueOf(fields[2].trim()));
        }).assignTimestampsAndWatermarks(WatermarkStrategy.<Event>forBoundedOutOfOrderness(Duration.ofSeconds(2))
                .withTimestampAssigner(new SerializableTimestampAssigner<Event>() {
                    @Override
                    public long extractTimestamp(Event element, long recordTimestamp) {
                        return element.timestamp;
                    }
                }));

        DataStream<Event> stream2 = streamSource2.map(data -> {
            String[] fields = data.split(" ");
            return new Event(fields[0].trim(), fields[1].trim(), Long.valueOf(fields[2].trim()));
        }).assignTimestampsAndWatermarks(WatermarkStrategy.<Event>forBoundedOutOfOrderness(Duration.ofSeconds(5))
                .withTimestampAssigner(new SerializableTimestampAssigner<Event>() {
                    @Override
                    public long extractTimestamp(Event element, long recordTimestamp) {
                        return element.timestamp;
                    }
                }));

        //Union合流操作
        stream1.union(stream2)
                .process(new ProcessFunction<Event, String>() {
                    @Override
                    public void processElement(Event value, ProcessFunction<Event, String>.Context ctx, Collector<String> out) throws Exception {
                        out.collect("水位线:" + ctx.timerService().currentWatermark());
                    }
                }).print();

        stream1.print("stream1");
        stream2.print("stream2");

        env.execute();
    }
}

这里需要考虑一个问题。在事件时间语义下,水位线是时间的进度标志;不同的流中可能水位线的进展快慢完全不同,如果它们合并在一起,水位线又该以哪个为准呢?还以要考虑水位线的本质含义,是“之前的所有数据已经到齐了”;所以对于合流之后的水位线,也是要以最小的那个为准,这样才可以保证所有流都不会再传来之前的数据。换句话说,多流合并时处理的时效性是以最慢的那个流为准的。我们自然可以想到,多条流的合并,某种意义上也可以看作是多个并行任务向同一个下游任务汇合的过程。

用 union 将两条流合并后,用一个 ProcessFunction来进行处理,获取当前的水位线进行输出。我们会发现两条流中每输入一个数据,合并之后的流中都会有数据出现;而水位线只有在两条流中水位线最小值增大的时候,才会真正向前推进。我们可以来分析一下程序的运行:在合流之后的 ProcessFunction 对应的算子任务中,逻辑时钟的初始状态

在这里插入图片描述
由于 Flink 会在流的开始处,插入一个负无穷大(Long.MIN_VALUE)的水位线,所以合流后的 ProcessFunction 对应的处理任务,会为合并的每条流保存一个“分区水位线”,初始值都是 Long.MIN_VALUE;而此时算子任务的水位线是所有分区水位线的最小值,因此也是Long.MIN_VALUE。

有兴趣的读者可自行测试

连接(Connect)

流的联合虽然简单,不过受限于数据类型不能改变,灵活性大打折扣,所以实际应用较少出现。除了联合(union),Flink 还提供了另外一种方便的合流操作——连接(connect)。顾名思义,这种操作就是直接把两条流像接线一样对接起来。

为了处理更加灵活,连接操作允许流的数据类型不同。但我们知道一个 DataStream 中的数据只能有唯一的类型,所以连接得到的并不是 DataStream,而是一个“连接流”(ConnectedStreams)。连接流可以看成是两条流形式上的“统一”,被放在了一个同一个流中;事实上内部仍保持各自的数据形式不变,彼此之间是相互独立的。

在这里插入图片描述
测试代码如下:Gitee中的代码

public class ConnectTest {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);


        DataStreamSource<Integer> stream1 = env.fromElements(1, 2, 3, 4);
        DataStreamSource<Long> stream2 = env.fromElements(5L, 6L, 7L, 8L);
				
		//connect连接,实现CoMapFunction函数转换
        stream1.connect(stream2)
                .map(new CoMapFunction<Integer, Long, String>() {
                    @Override
                    public String map1(Integer value) throws Exception {
                        return "Integer" + value.toString();
                    }

                    @Override
                    public String map2(Long value) throws Exception {
                        return "Long" + value.toString();
                    }
                }).print();

        env.execute();
    }
}

注意:只能连接两条流,如果连接多条流,建议自己做一个接口实现。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Flink是一个流式处理框架,它提供了丰富的数据流操作和转换功能。在Flink中,数据流类型转换是指将一个数据流从一种类型转换为另一种类型的操作。 Flink中的数据流类型转换可以通过以下几种方式实现: 1. Map转换:Map转换是最常用的数据流类型转换操作之一。它接收一个输入数据流,并将每个元素通过用户定义的函数进行转换,生成一个新的数据流。 2. FlatMap转换:FlatMap转换与Map转换类似,但它可以生成零个、一个或多个输出元素。它接收一个输入数据流,并将每个元素通过用户定义的函数进行转换,生成一个或多个新的数据流。 3. Filter转换:Filter转换用于过滤掉不符合条件的元素。它接收一个输入数据流,并根据用户定义的条件函数对每个元素进行判断,只保留满足条件的元素,生成一个新的数据流。 4. KeyBy转换:KeyBy转换用于按照指定的键对数据流进行分组。它接收一个输入数据流,并根据用户指定的键选择对应的字段作为分组依据,生成一个分组后的数据流。 5. Reduce转换:Reduce转换用于对分组后的数据流进行聚合操作。它接收一个分组后的数据流,并根据用户定义的聚合函数对每个分组进行聚合操作,生成一个新的数据流。 6. Window转换:Window转换用于将无界的数据流切分为有界的窗口,并对每个窗口内的数据进行操作。它接收一个输入数据流,并根据用户指定的窗口规则将数据流切分为窗口,然后对每个窗口内的数据进行操作,生成一个新的数据流。 以上是Flink中常用的数据流类型转换操作,通过这些转换操作,可以实现对数据流的各种处理和转换。如果你有其他相关问题,请继续提问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值