1.前言
在之前的文章当中,我针对于Flink中状态相关的一系列内容按照我自己理解的方式进行了讲述,但是单纯从我写的内容上看,貌似都是围绕着单条流进行计算的,所以在感官上就很不全面,所以我就准备在这篇文章当中,来跟大家讲讲Flink中的多流转换。虽然明面上叫做多留转换,实际上就是对数据流的分流与合流操作,分流就是将数据流中的数据按照对应的规则进行拆分,拆分出来的数据流的数量没有特定,具体还是要看代码逻辑。合流操作就比较精彩了,可以分为联合(union)、连接(connect)以及联结(join),而联结也会分成窗口联结、间隔联结、同组联结这三种不同的类型。接下来咱们就按照这种模式,一点一点的对多流合并这一段功能进行讲述吧。
2.分流
在前言中也说了,多流转换实际上就是分流与合流操作的总称,那最开始就先讲讲分流操作吧。
分流操作比较简单,是需要通过对程序中定义不同的拆分逻辑,将原本的DataStream分成多个不同的平级的DataStream,在这个操作中,需要引入一个叫做侧输出流的概念,我们在代码处理的过程中,可以通过按照自己编写的代码逻辑,来新建出对应数量的侧输出流,然后通过筛选逻辑,将拆分的数据流放入到对应的侧输出流中。然后通过对主流调用对应的方法,即可获得。值得注意的是,这里说的侧输出流虽然名字上叫做侧输出,实际上与主流没有差别,彼此之间都是平级的。
3.合流
分流操作完成之后就说说合流,合流操作与分流操作相比就要丰富很多了,毕竟在做需求的时候,有很多业务场景需要进行各种join操作,因此丰富一些也很正常。
3.1 联合(union)
Union这种联合方式,是最简单的一种合流操作,并且也很好理解。它所做的,就是将很多条流合并成一条流,然后对这条流里面的数据进行统一的处理。不过它有一点点限制,就是要求流中流中的数据类型必须要是一样的。
但是Union还有一个需要考虑的点,就是水位线问题。因为多条流中的数据都汇入到这一条流中,那也就证明属于每条流各自的水位线也进入到了这条汇总流中了。我们知道,水位线是流淌在数据流中的一个用于进行时间判断的标志,所以在Unioned流中,要以最慢的那条流为基准,也可以把union场景想象成为多并行度操作在下游进行合并的过程。
3.2 连接(Connect)
union虽然能够将多条流合并在一起,但是要求多条流中的数据类型要是一样的,这就会导致某些场景之下很难对数据做对应的处理。所以便有Connect算子来实现不同类型的数据流的合并操作。只不过它也有一个要求,就是要求Connected数据流只能是两条不同的数据流汇集而成,再多就不行了。并且在进行连接后流的处理的时候,处理函数内部需要实现两个不同的计算逻辑,因为是对合并前两条流分别进行处理。
并且这个Connected流能够直接调用keyby进行按键分区操作,得到的依然还是一个connected流。值得注意的是要求这两条流中的键值必须是一样的。
此外,Connect还能够帮助广播流进行连接,在声明出广播流的时候,可以调用Connect方法将两条流连接在一起。然后按照是否为keyed流进行各自的处理操作。
4.联结(Join)
联结Join是比之前union、connect更加好用丰富的功能,它能够按照传入的两条数据流中key相同的数据做匹配处理,然后针对匹配成功后的数据做相应的操作,某种场景之下,它比connect还要方便。并且也正是因为它能力强,所以花样也比较多一点,可以分为:窗口联结、间隔联结、同组联结,接下来咱们就分别进行一下讲解。
4.1 窗口联结
故名思意,窗口联结肯定是与窗口相关的操作。在这种计算逻辑下,是将要联结的两条流中的在一个窗口范围内的所有数据进行join计算,它是将窗口范围内的所有数据做一个堆积,然后在窗口触发的时候做笛卡尔积判断,所有join上的数据才回去调用窗口联结函数apply方法中传入的计算逻辑。具体的模式如下:
stream1.join(stream2)
.where() //主流的key选择器
.equalTo() //辅流的key选择器
.window() //窗口选择器,会话、滑动、滚动
.apply() //传入匹配上的数据的处理逻辑
4.2 间隔联结
窗口联结能力很强,但是会有个弊端,那就是如果网络延迟的事情发生,导致本来能匹配上的数据正好卡在了两个窗口的分界线上,那岂不是悲惨?所以就引入了间隔联结的方法,来维护错误场景的出现。
那间隔联结具体是怎么使用的呢?在join操作的时候,是有两条流的。如果网络发生延迟,或者业务本身有延迟,那进行window join就会发生要匹配的数据刚刚好的卡在两个不同窗口里面,就导致匹配失败。所以这个时候使用间隔联结,来针对主流的每一条数据的时间戳,在辅流上开一个范围,使得每一个主流中的数据都能够对这个范围的所有辅流中的数据做匹配,一旦匹配成功就会返回,然后被处理函数处理进行计算。具体的使用方法如下:
stream1
.keyBy()
.intervalJoin(stream2.keyBy())
.between(Time.milliseconds(-2), Time.milliseconds(1))
.process (new ProcessJoinFunction out) {
out.collect(left + "," + right);
}
});
4.3 同组联结
这种联结方式与window join非常类似,但是又比它灵活。它是在窗口的范围内,将这一窗口范围内的数据集传入到处理函数中,然后通过自定义逻辑的方式进行匹配,什么数据要保留,什么数据要消除都由开发者来进行。也就是说,它能够实现窗口函数和间隔函数这种inner join 无法实现的left join 和 right join以及outer join等操作。
stream1.coGroup(stream2)
.where()
.equalTo()
.window(TumblingEventTimeWindows.of(Time.hours(1)))
.apply()
//在实现方法上也和window join差不多
5.结尾
在本文章当中,主要表明了Flink中多流转换的这个功能,如果认真看的话会发现我说的话都很直白,很容易被理解。鉴于我在本章当中提到了窗口,那就在下一篇文章当中,聊一聊与窗口有关的内容吧。