SlidingWindows用于Apache Beam上的慢速数据(大间隔)

时间:2018-05-29 07:16:44

标签: java google-cloud-dataflow apache-beam sliding-window

我正在使用Chicago Traffic Tracker数据集,每15分钟发布一次新数据。当新数据可用时,它表示距离实时"实时" 10-15分钟的记录。 (example,查找_last_updt)。

例如,在00:20,我得到数据时间戳00:10;在00:35,我从00:20开始;在00:50,我从00:40开始。所以我可以获得新数据的时间间隔"固定" (每15分钟一次),虽然时间戳的间隔略有变化。

我正在尝试在Dataflow(Apache Beam)上使用这些数据,为此我正在使用Sliding Windows。我的想法是收集和处理4个连续的数据点(4 x 15min = 60min),并且一旦新的数据点可用,理想情况下更新我的和/平均值的计算。为此,我开始使用代码:

PCollection<TrafficData> trafficData = input        
    .apply("MapIntoSlidingWindows", Window.<TrafficData>into(
        SlidingWindows.of(Duration.standardMinutes(60)) // (4x15)
            .every(Duration.standardMinutes(15))) .     // interval to get new data
        .triggering(AfterWatermark
                        .pastEndOfWindow()
                        .withEarlyFirings(AfterProcessingTime.pastFirstElementInPane()))
        .withAllowedLateness(Duration.ZERO)
        .accumulatingFiredPanes());

不幸的是,当我从输入中收到新的数据点时,我没有从GroupByKey获得新的(更新的)结果。

我的SlidingWindows有问题吗?或者我错过了其他什么?

2 个答案:

答案 0 :(得分:2)

一个问题可能是水印越过窗口的末尾,并丢弃所有后来的元素。您可以尝试在水印通过后几分钟:

PCollection<TrafficData> trafficData = input        
    .apply("MapIntoSlidingWindows", Window.<TrafficData>into(
        SlidingWindows.of(Duration.standardMinutes(60)) // (4x15)
            .every(Duration.standardMinutes(15))) .     // interval to get new data
        .triggering(AfterWatermark
                        .pastEndOfWindow()
                        .withEarlyFirings(AfterProcessingTime.pastFirstElementInPane())
                        .withLateFirings(AfterProcessingTime.pastFirstElementInPane()))
        .withAllowedLateness(Duration.standardMinutes(15))
        .accumulatingFiredPanes());

让我知道这是否有帮助。

答案 1 :(得分:1)

所以@Pablo(根据我的理解)给出了正确的答案。但我有一些不适合评论的建议。

我想问你是否需要推拉窗户?据我所知,固定窗口可以为您完成工作并且计算也更简单。由于您正在使用累积触发窗格,因此您不需要使用滑动窗口,因为您的下一个DoFn函数已经从累积窗格中进行平均。

至于代码,我对早期和晚期的触发逻辑进行了更改。我还建议增加窗口大小。由于您知道数据每15分钟一次,因此您应该在15分钟后关闭窗口而不是15分钟。但你也不想选择一个最终会与15的倍数(如20)碰撞的窗口,因为在60分钟你会遇到同样的问题。因此,选择一个与15相关的数字,例如19.也允许延迟输入。

bash script.sh -f "temp.txt"

如果能解决您的问题,请告诉我们!

修改

所以,我无法理解你是如何计算上面的例子的,所以我使用的是一个通用的例子。以下是通用平均函数:

    PCollection<TrafficData> trafficData = input        
        .apply("MapIntoFixedWindows", Window.<TrafficData>into(
            FixedWindows.of(Duration.standardMinutes(19)) 
                        .triggering(AfterWatermark.pastEndOfWindow()
                            // fire the moment you see an element 
                            .withEarlyFirings(AfterPane.elementCountAtLeast(1))
                            //this line is optional since you already have a past end of window and a early firing. But just in case 
                            .withLateFirings(AfterProcessingTime.pastFirstElementInPane()))
                        .withAllowedLateness(Duration.standardMinutes(60))
                        .accumulatingFiredPanes());

要运行它,您需要添加以下行:

public class AverageFn extends CombineFn<Integer, AverageFn.Accum, Double> {
  public static class Accum {
    int sum = 0;
    int count = 0;
  }

  @Override
  public Accum createAccumulator() { return new Accum(); }

  @Override
  public Accum addInput(Accum accum, Integer input) {
      accum.sum += input;
      accum.count++;
      return accum;
  }

  @Override
  public Accum mergeAccumulators(Iterable<Accum> accums) {
    Accum merged = createAccumulator();
    for (Accum accum : accums) {
      merged.sum += accum.sum;
      merged.count += accum.count;
    }
    return merged;
  }

  @Override
  public Double extractOutput(Accum accum) {
    return ((double) accum.sum) / accum.count;
  }
}

由于您使用累积触发触发器 当前 ,这将是解决该解决方案的最简单的编码方式。

但是,如果您想使用丢弃火窗格窗口,则需要使用PCollection<Double> average = trafficData.apply(Combine.globally(new AverageFn())); 来存储之前的平均值,并将其作为旁边输入传递给下一个平均值,以便跟踪值。这在编码方面稍微复杂一些,但肯定会提高性能,因为每个窗口都会进行不断的工作,这与累积点火不同。

这是否足以让您生成自己的功能以丢弃火窗格窗口?