目录:apache beam 个人使用经验总结目录和入门指导(Java)

输入时刻概念

对于beam里的数据集里的每个数据,都会附带1个Instant成员(以下称为输入时刻),指明了该条数据输入时的时间戳(单位毫秒)。
如果是有限数据集(批处理方式输入的),且未没手动设置输入时刻, 则默认输入时刻全部相同。
如果是无限流式数据集,例如kafka,会根据到达的时间来设置输入时刻。
窗口则就是根据输入时刻来进行划分的。

伪造流式数据

因为用kafka做输入数据时,不好精确地控制输入时间
所以理解窗口概念和举例之前,我们先学习如何伪造流式数据。、

首先,我们造如下1个KV列表:

<KV<Integer, String>> kvList = new ArrayList<>();
kvList.add(KV.of(999,"0.999s"));
kvList.add(KV.of(1000,"1.0s"));
kvList.add(KV.of(1001,"1.001s"));
kvList.add(KV.of(1999,"1.999s"));
kvList.add(KV.of(2000,"2.0s"));
kvList.add(KV.of(2500,"2.5s"));
kvList.add(KV.of(3000,"3.0s"));
kvList.add(KV.of(4000,"4.0s"));
kvList.add(KV.of(5000,"5.0s"));

里面的key就代表了输入时刻,value是字符串展示。
接着用withTimestamps给这些输入元素设置输入时间(单位毫秒),并把kv转成v

PCollection<String> pTimeStr = pipeline.apply(Create.of(kvList))
// 给每个输入元素设置Instant为(某个固定时间 + key)
.apply(WithTimestamps.of(new SimpleFunction<KV<Integer, String>, Instant>() {
@Override
public Instant apply(KV<Integer, String> input) {
return new Instant(NOW_TIME + input.getKey());
}
}))
// 把Key-value转成value
.apply(Values.create());

这样1个流式数据就伪造好了, 我们就可以忽略绝对时间,只考虑相对的那些时间(即与NOW_TIME相对的时间间隔)

窗口

窗口的使用为pcollection.apply(Window.into(WindowsFn))
我们的例子中,会根据设置的窗口,把窗口内的数据合成1个列表并输出。

  • 合成列表的transform使用Combine.globally(Sample.<String>anyCombineFn(100)).withoutDefaults()
  • 而输出的话,我们会打印出列表并且加上该窗口内输入时刻的打印。
    代码如下,后面我们介绍3种窗口时就只展示pTimeStrByWindow的apply过程:
<String> pTimeStrByWindow = pTimeStr.apply(Window.into(某个windowsFn);
// 同1个窗口内的合成1个列表,最多聚合100个
pTimeStrByWindow.apply(Combine.globally(Sample.<String>anyCombineFn(100)).withoutDefaults())
// 输出
.apply(ParDo.of(new PrintStrFn()));

pipeline.run().waitUntilFinish();

固定时间窗口

固定时间窗口表示数据流中一致的连续、不重叠的时间间隔。

最简单的窗口形式开水奇偶固定时间窗口:如下图所示,有1个持续更新的时间戳PCollection,每个窗口可以捕获(例如)所有时间戳在30s时间间隔内的元素。

apache beam入门之窗口概念_时间间隔

我们的例子里以1秒为固定窗口时间。
则显然分组情况应给为0.999一组, 1.0、1.001、1.999为一组,以此类推。

固定窗口的windowsFn为FixedWindows
固定间隔通过Duration去设置,里面还可以设置分钟间隔、小时间隔等(standardXXX())。

固定窗口的代码如下:

<String> pTimeStrByWindow = pTimeStr.apply(Window.into(FixedWindows.of(Duration.standardSeconds(1))));

打印结果如下:

apache beam入门之窗口概念_数据_02


结果正确

滑动时间窗口

滑动时间窗口也表示数据流中的时间间隔; 然而,滑动时间窗口可以重叠。 例如,每个窗口可能捕获五分钟的数据,但是每十秒钟会启动一个新窗口。 滑动窗口开始的频率称为周期。 因此,示例中的窗口的时间长度为5分钟,滑动周期为10秒钟。

由于多个窗口重叠,数据集中的大多数元素将属于多个窗口。 这种窗口对于计算不断变化的数据的均值非常有用; 使用滑动时间窗口,可以在示例中计算过去5分钟的数据的运行平均值,每10秒更新一次。

apache beam入门之窗口概念_时间间隔_03

本文中以2秒窗口、滑动周期为1秒做例子。
对于之前所提到的0.999、1、1.001、1.999、2.0、2.5、3.0、4.0、5.0这串流式数据
所划分的窗口区间如下:

  • 0.999属于[-1s,1s)区间
  • 0.999、1、1.001、1.999属于[0s,2s)区间
  • 1、1.001、1.999、2.0、2.5属于[1s,3s)区间
  • 2.0、2.5、3.0属于[2s,4s)区间
  • 3.0、4.0属于[3s,5s)区间
  • 4.0、5.0属于[4s,6s)区间
  • 5.0属于[5s,7s)区间

因此最终应该会划分出7个窗口。

滑动窗口windowsFn使用SlidingWindows.of(窗口总长).every(窗口启动间隔时间),代码如下:

<String> pTimeStrByWindow = pTimeStr.apply(
Window.into(SlidingWindows.of(Duration.standardSeconds(2))
.every(Duration.standardSeconds(1))));

且窗口总长要大于窗口启动间隔时间,否则会报错,因为总厂比间隔小的话,会有流式数据丢失的情况。
结果打印如下:

apache beam入门之窗口概念_时间间隔_04

与前面列出的一致。

会话时间窗口

会话窗口是一种在时间上非连续的窗口。 窗口的起始点以收到第一条数据开始, 直到x秒(会话结束间隔)内没有收到新的数据为结束点。 这种窗口适用于一些非周期规律性的流式数据

以本文例子为例0,对于0.999、1、1.001、1.999、2.0、2.5、3.0、4.0、5.0这串数据
0,999是第一次收到的数据,以此时为起始点,直到3.0s才结束, 因为3.0和4.0的间隔已经超过1秒的差距了。
之后4.0开始为起始点开始新的窗口,因为与下一个数据5.0s相差1秒所以直接结束
同理5.0也是1个窗口
因此会分成3个窗口:

  • 0.999、1、1.001、1.999、2.0、2.5、3.0
  • 4.0
  • 5.0

会话窗口使用Sessions.withGapDuration(Duration.standardSeconds(会话结束间隔))
代码如下:

<String> pTimeStrByWindow = pTimeStr.apply(Window.into(Sessions.withGapDuration(Duration.standardSeconds(1))));

打印结果如下:

apache beam入门之窗口概念_数据集_05

正确。

单一全局窗口

任何非流式的批处理有限数据集默认都是单一全局窗口, 即所有数据都在1个窗口内。
如果要对把某流式数据放进单一全局窗口,前提是你确认这一次计算过程中, 你是知道流式数据会在某个触发条件下结束,这涉及到触发器概念,因此这一块放到​apache beam入门之触发器​中去讲
单一全局窗口实现方式:

PCollection<String> items = ...;
PCollection<String> batch_items = items.apply(
Window.<String>into(new GlobalWindows()));