为什么我的处理时间窗口会触发但事件时间却无法触发

时间:2019-08-06 03:03:04

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

我正在努力获取基于事件时间的触发器来触发我的apache光束管道,但确实能够触发具有处理时间的窗口触发。

我的管道相当基本:

  1. 我从pubsub读入的数据点包括毫秒级时间戳,这些时间戳稍早于最早的批处理数据点。批量处理数据以减少客户端的工作量和发布费用。

  2. 我提取第二级时间戳并将时间戳应用于各个数据点

  3. 我在窗口中处理数据,并避免使用全局窗口。

  4. 我按秒对数据进行分组,以便以后按流数据按秒进行分类。

  5. 我最终在分类秒数上使用滑动窗口,有条件地每秒发送两次消息之一以pubsub。

我的问题似乎在步骤3中。

我正在尝试在第3阶段使用最终将在第5阶段使用的相同的窗口化策略,以对分类的秒数进行滑动平均值计算。

我试图弄乱withTimestampCombiner(TimestampCombiner.EARLIEST)选项,但这似乎无法解决。

我已经了解了与事件时间一起使用的.withEarlyFirings方法,但这似乎可以模仿我现有的解决方法。理想情况下,我将能够依靠通过窗口结尾的水印并包含延迟触发。

// De-Batching The Pubsub Message

  static public class UnpackDataPoints extends DoFn<String,String>{
    @ProcessElement
        public  void processElement(@Element String c, OutputReceiver<String> out) {
            JsonArray packedData = new JsonParser().parse(c).getAsJsonArray();
            DateTimeFormatter dtf = DateTimeFormat.forPattern("EEE dd MMM YYYY HH:mm:ss:SSS zzz");
            for (JsonElement acDataPoint: packedData){
                String hereData = acDataPoint.toString();
                DateTime date = dtf.parseDateTime(acDataPoint.getAsJsonObject().get("Timestamp").getAsString());
                Instant eventTimeStamp = date.toInstant();
                out.outputWithTimestamp(hereData,eventTimeStamp);
            }
        }
        }
// Extracting The Second
 static public class ExtractTimeStamp extends DoFn<String,KV<String,String>> {
    @ProcessElement
        public void processElement(ProcessContext ctx ,@Element String c, OutputReceiver<KV<String,String>> out) {
            JsonObject accDataObject = new JsonParser().parse(c).getAsJsonObject();
            String milliString = accDataObject.get("Timestamp").getAsString();
            String secondString = StringUtils.left(milliString,24);
            accDataObject.addProperty("noMiliTimeStamp", secondString);
            String updatedAccData = accDataObject.toString();
            KV<String,String> outputKV = KV.of(secondString,updatedAccData);
                    out.output(outputKV);
    }
    }
// The Pipeline & Windowing
   Pipeline pipeline = Pipeline.create(options);

 PCollection<String> dataPoints = pipeline
    .apply("Read from Pubsub", PubsubIO.readStrings()
                    .fromTopic("projects/????/topics/???")
                    .withTimestampAttribute("messageTimestamp"))
   .apply("Extract Individual Data Points",ParDo.of(new UnpackDataPoints()));


 /// This is the event time window that doesn't fire for some reason
        /*
        PCollection<String> windowedDataPoints = dataPoints.apply(
                Window.<String>into(SlidingWindows.of(Duration.standardSeconds(5)).every(Duration.standardSeconds(1)))
               // .triggering(AfterWatermark.pastEndOfWindow())
               .withEarlyFirings(AfterProcessingTime.pastFirstElementInPane()
                .plusDelayOf(TWO_MINUTES))
                //.triggering(AfterProcessingTime.pastFirstElementInPane().plusDelayOf(Duration.standardSeconds(2)))
                .discardingFiredPanes()
                .withTimestampCombiner(TimestampCombiner.EARLIEST)
                .withAllowedLateness(Duration.standardSeconds(1)));
        */
     ///// Temporary Work Around, this does fire but data is out of order

        PCollection<String> windowedDataPoints = dataPoints.apply(
                Window.<String>into(FixedWindows.of(Duration.standardMinutes(120)))
                .triggering(
                        AfterProcessingTime.pastFirstElementInPane()
                                .plusDelayOf(Duration.standardSeconds(5)))
                .discardingFiredPanes()
                .withTimestampCombiner(TimestampCombiner.EARLIEST)
                        .withAllowedLateness(Duration.standardSeconds(1)));

  PCollection<KV<String, String>> TimeStamped = windowedDataPoints
                .apply( "Pulling Out The Second For Aggregates", ParDo.of(new ExtractTimeStamp()));

        PCollection<KV<String, Iterable<String>>> TimeStampedGrouped = TimeStamped.apply("Group By Key",GroupByKey.create());

        PCollection<KV<String, Iterable<String>>> testing = TimeStampedGrouped.apply("testingIsh", ParDo.of(new LogKVIterable()));

当我使用第一个被注释掉的窗口策略时,我的管道会无限期地运行以接收数据并且LogKVIterable ParDo永远不会返回任何内容,而当我使用处理时间时,LogKVIterable会触发并记录到控制台。

2 个答案:

答案 0 :(得分:1)

这看起来确实像是您要添加到数据中的时间戳记错误/损坏。我鼓励您验证以下内容:

  1. 已正确添加元素中的时间戳。在转换之前/之后添加一些日志记录,并广泛测试该代码。

  2. 管道中的数据新鲜度和系统延迟指标正在按预期进行。 如果数据新鲜度未按预期运行,则表明您的时间戳记设置不正确。

答案 1 :(得分:0)

在处理时间上触发与在事件时间上触发不同。在处理时间中,没有诸如延迟数据之类的东西。在事件发生时,处理最新数据是真正的挑战。 事件时间处理中的后期数据通过使用水印和触发器来处理。有关此方面的出色指南,我建议您阅读Googler Tyler Akidau的这两篇文章:ab

由于在处理时间窗口中,没有延迟数据之类的东西,因此您的处理时间 Apache Beam管道可以正常工作没有任何问题。

与此同时,在事件时间窗口中,可能会出现延迟数据,并且您的窗口和触发应通过良好的设计来处理这些情况。

您的事件时间处理管道代码很可能由于配置错误而无法触发!由于您的水印(对于发布/订阅来源)是通过启发式方式确定的,因此我无法重现您的问题。尽管我建议您通过以下方式调试代码: 首先-增加allowLatness。例如:1小时。如果可行,那就太好了!如果不是,请参阅第二。 第二-用EarlyFirings注释掉。如果可行,那就太好了!如果没有,请取消注释并查看三 三-使用固定时间窗口而不是滑动时间窗口。

继续调试,直到您能够找出问题为止

:):)