简体   繁体   English

管道外的Java代码无法在Dataflow上运行

[英]Java code outside of the pipeline won't run on Dataflow

Looks like any code outside the pipe line won't be run on Dataflow. 看起来管道之外的任何代码都不会在Dataflow上运行。 In the following example I get a NullPointerException for TableSchema in the TableRowConverterFn.processElement method. 在下面的示例中,我在TableRowConverterFn.processElement方法中获得了TableSchemaNullPointerException What is the right way to do this with Apache Beam/Dataflow? 用Apache Beam / Dataflow做到这一点的正确方法是什么?

     private static TableSchema TableSchema;

     public static void main(String[] args) {

        try {
            TableSchema = TableSchemaReader.read(TableSchemaResource);
        } catch (IOException e) {
            log.error("Table schema can not be read from {}. Process aborted.", TableSchemaResource);
            return;
        }

        DataflowDfpOptions options = PipelineOptionsFactory.fromArgs(args)
                //.withValidation()
                .as(DataflowDfpOptions.class);

        Pipeline pipeline = Pipeline.create(options);

        Stopwatch sw = Stopwatch.createStarted();
        log.info("DFP data transfer from GS to BQ has started.");

        pipeline.apply("ReadFromStorage", TextIO.read()
                .from("gs://my-test/stream/*.gz")
                .withCompression(Compression.GZIP))
                .apply("TransformToTableRow", ParDo.of(new TableRowConverterFn()))
                .apply("WriteToBigQuery", BigQueryIO.writeTableRows()
                        .to(options.getTableId())
                        .withMethod(STREAMING_INSERTS)
                        .withCreateDisposition(CREATE_NEVER)
                        .withWriteDisposition(WRITE_APPEND)
                        .withSchema(TableSchema)); //todo: use withJsonScheme(String json) method instead


        pipeline.run().waitUntilFinish();

        log.info("DFP data transfer from GS to BQ is finished in {} seconds.", sw.elapsed(TimeUnit.SECONDS));
    }

    /**
     * Creates a TableRow from a CSV line
     */
    private static class TableRowConverterFn extends DoFn<String, TableRow> {

        @ProcessElement
        public void processElement(ProcessContext c) throws Exception {

            String[] split = c.element().split(",");

            //Ignore the header line
            //Since this is going to be run in parallel, we can't guarantee that the first line passed to this method will be the header
            if (split[0].equals("Time")) {
                log.info("Skipped header");
                return;
            }

            TableRow row = new TableRow();
            for (int i = 0; i < split.length; i++) {

                //This throws NEP!!!
                TableFieldSchema col = TableSchema.getFields().get(i);

                //String is the most common type, putting it in the first if clause for a little bit optimization.
                if (col.getType().equals("STRING")) {
                    row.set(col.getName(), split[i]);
                } else if (col.getType().equals("INTEGER")) {
                    row.set(col.getName(), Long.valueOf(split[i]));
                } else if (col.getType().equals("BOOLEAN")) {
                    row.set(col.getName(), Boolean.valueOf(split[i]));
                } else if (col.getType().equals("FLOAT")) {
                    row.set(col.getName(), Float.valueOf(split[i]));
                } else {
                    //Simply try to write it as a String if
                    //todo: Consider other BQ data types.
                    row.set(col.getName(), split[i]);
                }
            }
            c.output(row);
        }
    }

Althought this code might work locally in a DirectRunner, it indeed can't work in a DataflowRunner. 尽管此代码可能在DirectRunner中本地运行,但实际上在DataflowRunner中无法运行。 Here's why: 原因如下:

The DoFns created outside of your main function do not have access to your classes's (even static) variables with the DataflowRunner. main函数之外创建的DoFns 无法使用DataflowRunner访问类的(甚至是静态)变量。 This is, I believe (not 100% sure though), due to how Dataflow stages and serializes DoFns when running in the cloud. 我相信这是(尽管不是100%肯定),这是由于Dataflow在云中运行时如何分段和序列化DoFns。

Here's how you can overcome this issue: 解决问题的方法如下:

private static class TableRowConverterFn extends DoFn<String, TableRow> {
    private static TableSchema tableSchema;

    public TableRowConverterFn(TableSchema tableSchema) {
        this.tableSchema = tableSchema;
    }

    @ProcessElement
    public void processElement(ProcessContext c) throws Exception {
        // stuff
    }
}

Then in your main function call 然后在您的主函数调用中

.apply("TransformToTableRow", ParDo.of(new TableRowConverterFn(tableSchema)));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM