在运行时读取多个文件(数据流模板)

时间:2018-11-05 17:12:47

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

我正在尝试构建数据流模板。

目标是读取ValueProvider,它将告诉我要读取哪些文件。 然后,对于每个文件,我需要读取该对象并丰富其数据。 我已经尝试过了:

        p.apply(Create.of(options.getScheduleBatch()))
            .apply(ParDo.of(StringScheduleBatchToFileReceivedFn.of()))
            .apply(ParDo.of(new DoFn<FileReceived, PCollection<EventRow>>() {
                @ProcessElement
                public void process(ProcessContext c) {
                    FileReceived fileReceived = c.element();
                    Broker broker = configuration.getBroker(fileReceived.getBrokerId());
                    PCollection<EventRow> eventRows = p
                            .apply(TextIO.read().from(fileReceived.getUri()))
                            .apply(ParDo.of(StringToEventRowFn.of(broker, fileReceived, options.getJobName())));
                    c.output(eventRows);
                }
            }));

但是我有以下错误:

从CoderRegistry推断编码器失败:无法为org.apache.beam.sdk.values.PCollection提供编码器。

我想找到一种比使用gcs客户端自己读取文件更好的方法。

您有什么建议吗?

最诚挚的问候

1 个答案:

答案 0 :(得分:1)

问题:

您正在尝试发出PCollection作为ParDo的输出。这不起作用。

详细信息:

PCollection是一种抽象,表示元素的潜在无限集合。将转换应用于PCollection会给您另一个PCollection。可以应用的转换之一是ParDoParDos进行逐元素转换。当应用ParDo时,您要表达的是-“应用PCollection并通过应用ParDo来转换其中的所有元素来制作另一个”。

使处理有效的事情之一是能够并行执行所有操作,例如通过在每个执行节点上针对不同的元素运行相同的ParDo一次在多个执行节点(例如VM /机器)上转换大量元素。而且,您无法明确控制是在同一执行节点上还是在另一执行节点上进行任何特定的转换,这是如何优化此转换的基础系统设计的一部分。但是要启用此功能,您必须能够潜在地在执行节点之间传递元素并将其持久化以进行聚合。 Beam通过要求您为元素实现Coders来支持这一点。编码器是一种序列化机制,它允许Beam将元素(由Java对象表示)转换为字节数组,然后将其传递到下一个转换(可能在另一台计算机上发生)或存储。例如,Beam需要能够对从ParDo输出的元素进行编码。 Beam知道如何序列化某些类型,但是它无法自动推断所有内容,因此您必须为无法推断的内容提供编码器。

您的示例如下所示:取一些PCollection,然后通过对每个元素应用PCollection将其转换为另一个ParDo,并且ParDo会转换每个输入元素变成PCollection。这意味着一旦ParDo处理元素,就必须对其进行编码并将其传递给下一个转换。这里的问题是-如何编码(可能无界的)PCollection并将其传递给下一个转换,或将其持久化以进行聚合?

Beam目前不支持此功能,因此您需要选择另一种方法。

对于您的具体情况,我不确定是否可以在开箱即用的Beam中直接使用文件名流并将其转换为子管道以处理文件中的行。

解决方法:

我能想到的几种方法都可以绕过此限制:

  • 如果文件名具有已知模式,则可以在TextIO中指定模式,它可以在新文件到达时读取它们。

  • 如果它们没有已知的模式,则可以潜在地编写另一个管道来重命名文件名,以便它们具有通用名称模式,然后在另一个管道中的TextIO中使用该模式。 / p>

  • 如果可行(例如文件适合内存),则可以使用纯Java File API读取文件内容,将它们拆分为行,然后在单个ParDo中发出这些行。然后,您可以在以下StringToEventRowFn中应用相同的ParDo

希望这会有所帮助