Flink流计算编程--在双流中体会joinedStream与coGroupedStream

本文深入探讨Flink中joinedStream与coGroupedStream的区别和使用,通过实例展示了如何在双流中进行join操作。在SQL与Flink的join、coGroup对比中,揭示了在复杂逻辑处理上coGroup的优势,同时通过程序实践展示了如何在Flink中实现这两个操作,以及在结果处理上的灵活性。最后总结了两者在Flink流处理中的应用,并提到了未来Flink对SQL支持的增强。
摘要由CSDN通过智能技术生成

一、joinedStream与coGroupedStream简介

在实际的流计算中,我们经常会遇到多个流进行join的情况,Flink提供了2个Transformations来实现。
如下图:
这里写图片描述

注意:Join(Cogroups) two data streams on a given key and a common window。这里很明确了,我们要在2个DataStream中指定连接的key以及window下来运算。

二、SQL比较

我们最熟悉的SQL语言中,如果想要实现2个表join,可以如下实现:

select T1.* , T2.*
from T1 
join T2 on T1.key = T2.key;

这个SQL是一个inner join的形式。稍微复杂点的带有group by与order by的SQL如下:

select T1.key , sum(T2.col)
from T1 
join T2 on T1.key = T2.key
group by T1.key
order by T1.col;

通过这2个SQL,我们想要在Flink中实现实时的流计算,就可以通过joinedStream或coGroupedStream来实现。但是在join之后实施更复杂的运算,例如判断、迭代等,仅仅通过SQL实现,恐怕会很麻烦,只能通过PL/SQL块来实现,而Flink提供了apply方法,用户可以自己编写复杂的函数来实现。

三、join与coGroup的区别

先来看下源码中提供的类与方法比较下:
1、join
这里写图片描述
通过结构可以发现,在JoinedStreams提供了where方法,在where类中提供了equalTo方法,下一层就是window,之后是trigger、evictor以及apply方法。
这里贴出一个代码模板共参考:

 * val one: DataStream[(String, Int)]  = ...
 * val two: DataStream[(String, Int)] = ...
 *
 * val result = one.join(two)
 *     .where {t => ... }
 *     .equal {t => ... }
 *     .window(TumblingEventTimeWindows.of(Time.of(5, TimeUnit.SECONDS)))
 *     .apply(new MyJoinFunction())

2、coGroup
这里写图片描述
仔细观察我们发现,实现上与join几乎一样,唯一的区别在于apply方法提供的参数类型。
代码模板如下:

 * val one: DataStream[(String, Int)]  = ...
 * val two: DataStream[(String, Int)] = ...
 *
 * val result = one.coGroup(two)
 *     .where(new MyFirstKeySelector())
 *     .equalTo(new MyFirstKeySelector())
 *     .window(TumblingEventTimeWindows.of(Time.of(5, TimeUnit.SECONDS)))
 *     .apply(new MyCoGroupFunction())

3、区别
刚才提到的apply方法中的参数类型不一样,join中提供的apply方法,参数是T1与T2这2种数据类型,对应到SQL中就是T1.* 与 T2.*的一行数据。而coGroup中提供的apply方法,参数是Iterator[T1]与Iterator[2]这2种集合,对应SQL中类似于Table[T1]与Table[T2]。

基于这2种方式,如果我们的逻辑不仅仅是对一条record做处理,而是要与上一record或更复杂的判断与比较,甚至是对结果排序,那么join中的apply方法显得比较困难。
这里写图片描述

四、程序实践

下面开始实际演示程序的编写与代码的打包并发布到集群,最后输出结果的一步步的过程。

说明:由于是双流,我模拟Kafka的Topic,自定义了2个socket,其中一个指定“transaction”的实时交易输入流,另一个socket指定“Market”的快照输入流,原则上每3秒(时间戳)生成1个快照。

1、join
由于是2个DataStream,且我的逻辑是要根据各自流产生的时间戳去限制window,因此这里要对2个流都分配时间戳并emit水位线(采用EventTime):

val eventMarketStream = marketDataStream.assignAscendingTimestamps(_._2)
val eventTransactionStream = transactionDataStream.assignAscendingTimestamps(_._2)

join操作后,apply方法接收的只是T1与T2的类型:

val joinedStreams = eventTransactionStream
      .join(eventMarketStream)
      .where(_._1)
      .equalTo(_._1)
      .window(TumblingEventTimeWindows.of(Time.seconds(10)))
      .apply{
        (t1 : (Long, Long, Long, Long), t2 : (Long, Long, Long), out : Collector[(Long,String,String,Long,Long,Long)]) =>

          val transactionTime = t1._2
          val marketTime = t2._2
          val differ = transactionTime - marketTime

          val format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")

          if(differ >=0 && differ <= 3 * 1000) {
            out.collect(t1._1,format.format(marketTime) + ": marketTime", format.format(transactionTime) + ": transactionTime",t2._3,t1._3,t1._4)
          }
      }

这里实现的逻辑就是每个key在10秒的EventTime窗口中join,且只需要那些交易时间在快照时间之后,且在3秒的间隔内的数据。

详细的代码如下:

import java.text.SimpleDateFormat

import org.apache.flink.api.common.functions.MapFunction
import org.apache.flink.streaming.api.TimeCharacteristic
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.windowing.assigners.TumblingEventTimeWindows
import org.apache.flink.streaming.api.windowing.time.Time
import org.apache.flink.util.Collector


/**
  * Created by zhe on 2016/6/21.
  */
object JoinedOperaion {
   
  case class Transaction(szWind
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值