【问题标题】:Cannot pass FileIO.Write/WriteFiles/WriteShardedBundlesToTempFiles/GroupIntoShards无法通过 FileIO.Write/WriteFiles/WriteShardedBundlesToTempFiles/GroupIntoShards
【发布时间】:2020-03-23 13:18:26
【问题描述】:

我实现了从 Pub/Sub 读取包含 AWS S3 密钥的 JSON 的管道;并下载由换行符分隔的 JSON (NDJson) 组成的 S3 对象;然后,使用窗口和 GroupByKey 将 NDJson 写入 GCS 中的文件。

显然,它正在以 10 秒的窗口和AfterPane.elementCountAtLeast(1000). 进行流式传输,但是,窗口没有被分割,并且 GCS 中没有文件。此外,还发生了异常Shutting down JVM after 8 consecutive periods of measured GC thrashing. Memory is used/total/max = 4295/4946/4946 MB, GC last/max = 98.54/98.54 %, #pushbacks=1, gc thrashing=true. Heap dump not written.

下图是我的 Dataflow 的作业图。 GroupIntoShards 不输出数据。我不明白为什么会这样。此外,这是发生未绑定集。换句话说,如果 Pub/Sub 消息的数量很少(可能不发生 GC 就足够了),结果是好的。原因可能是关闭窗口,因为 Pub/Sub 的订阅没有任何未确认的消息。但是,如果消息被连续放置,GroupIntoShards 不起作用并且 GCS 中没有文件。 enter image description here

以下是我的部分来源:

在主类中,

public static PipelineResult run(Options options) {
    Pipeline pipeline = Pipeline.create(options);

    PCollection<String> inputed =
        .apply("Read PubSub Events", PubsubIO.readMessagesWithAttributes().fromTopic(options.getInputTopic()))
        .apply("NDJson Divider", ParDo.of(new NDJsonDivider(options.getSecretId())));

    inputed
        .apply(options.getWindowDuration() + " Window",
            Window.<String>into(FixedWindows.of(DurationUtils.parseDuration(options.getWindowDuration())))
                .triggering(
                    Repeatedly.forever(
                        AfterFirst.of(
                            AfterWatermark.pastEndOfWindow(),
                            AfterPane.elementCountAtLeast(1000),
                            AfterProcessingTime.pastFirstElementInPane().plusDelayOf(DurationUtils.parseDuration(options.getWindowDuration())))))
                .accumulatingFiredPanes()
                .withAllowedLateness(Duration.standardDays(2))
        )
        .apply(FileIO.<String, String>writeDynamic()
            .by(new TableRowPartitionContextFn())
            .via(TextIO.sink())
            .to(options.getOutputDirectory())
            .withNaming(PartitionedFileNaming::new)
            .withNumShards(options.getNumShards())
            .withDestinationCoder(StringUtf8Coder.of())
            .withCompression(Compression.GZIP)
        );

    return pipeline.run();
  }

NDJsonDivider 类中,


  @ProcessElement
  public void processElement(ProcessContext c) {
    JsonObject msg;
    try {
      msg = new JsonParser().parse(new String(c.element().getPayload())).getAsJsonObject();
    } catch (JsonSyntaxException | IllegalStateException e) {
      LOG.error(e);
      return;
    }

    if (msg.has(RECORDS) && msg.get(RECORDS).isJsonArray()) {
      JsonArray records = msg.get(RECORDS).getAsJsonArray();
      try (BufferedReader reader = processPutEventMessage(records)) {
        if (reader == null) {
          return;
        }
        String line;
        while (Objects.nonNull(line = reader.readLine())) {
          c.outputWithTimestamp(line, Instant.now());
        }
      } catch (IOException e) {
        LOG.error("Failed to process Put Event Message: ", e);
      }
    }
  }

  private BufferedReader processPutEventMessage(JsonArray records) {
    try {
      putEventMessage s3Obj = extractS3ObjectInfo(records);
      GCPBridge.getInstance().setSecretId(this.secretId);
      return S3.getReader(s3Obj.region, s3Obj.bucket, s3Obj.key, "UTF-8");
    } catch (IllegalArgumentException | AWSS3Exception | NoSuchKeyException e) {
      LOG.error("Failed to access S3:", e);
    }
    return null;
  }

TableRowPartitionContextFn 类中,

class TableRowPartitionContextFn implements SerializableFunction<String, String> {
  @Override
  public String apply(String e) {
    JsonObject data = new JsonParser().parse(e).getAsJsonObject();
    String Id = "", Type = "";
    if (data.has("id")) {
      Id = data.get("id").getAsString();
    }
    if (data.has("type")) {
      Type = data.get("type").getAsString();
    }
    return Id + "/" + Type;
  }
}
mvn -Pdataflow-runner compile exec:java -Dexec.mainClass=com.dev.playground -Dexec.args="--project=PROJECTID --inputTopic=projects/PROJECTID/topics/dev --outputDirectory=gs://dev/playground/output/ --secretId=dev --tempLocation=gs://dev/playground/tmp/ --runner=DataflowRunner

默认设置持续时间 @Default.String("10s") String getWindowDuration();

【问题讨论】:

  • 那么问题是什么?
  • 您介意提供您用于运行管道的命令行吗?我想知道持续时间的选项是什么
  • @Mardoz 我想知道为什么数据集没有按窗口和 GroupByKey 划分。
  • @AlexAmato 命令为:mvn -Pdataflow-runner compile exec:java -Dexec.mainClass=com.dev.playground -Dexec.args="--project=PROJECTID --inputTopic=projects/PROJECTID/topics/dev --outputDirectory=gs://dev/playground/output/ --secretId=dev --tempLocation=gs://dev/playground/tmp/ --runner=DataflowRunner 默认设置时长@Default.String("10s") String getWindowDuration();

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


【解决方案1】:

我怀疑Repeatedly.forever().accumulatingFiredPanes() 的组合将永远存储数据的缓冲区,最终导致内存不足错误。

请尝试使用discardingFiredPanes(),它不会保留先前触发窗格的元素。

这意味着只有到达的新元素才会在触发时在窗格中发出。您似乎没有对窗格中的所有值进行任何类型的聚合/组合结果。看起来您只是将它们直接写入输出,所以我认为您不需要 accumulatingFiredPanes。

如果您想根据窗口中的所有元素(例如平均值、总和等)计算某种聚合统计数据或值。然后我会建议在窗口之后使用组合器来累积结果。

【讨论】:

  • 感谢您的宝贵时间。实际上,首先我使用discardingFiredPanes() 而不是accumulatingFiredPanes()。但是,出现了上述问题,我只是尝试使用accumulatingFiredPanes() 进行调试。因此,不幸的是,在这种情况下,使用 discardingFiredPanes() 不是解决方案。
  • 您能否尝试删除 .withAllowedLateness 以进一步调试。我想知道这是否会导致任何问题。还可以考虑将触发器简化为 AfterProcessingTime.alignedTo(duration),以调试并查看是否会产生任何结果。
  • 删除.withAllowedLateness() 时,出现java.lang.IllegalArgumentException: Except when using GlobalWindows, calling .triggering() to specify a trigger requires that the allowed lateness be specified using .withAllowedLateness() to set the upper bound on how late data can arrive before being dropped. 异常。
  • 我用了.triggering(Repeatedly.forever(AfterProcessingTime.pastFirstElementInPane().alignedTo(Duration.standardSeconds(10)))),但还是出现了同样的问题。
猜你喜欢
  • 1970-01-01
  • 2020-08-21
  • 1970-01-01
  • 2012-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多