[英]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
方法中获得了TableSchema
的NullPointerException
。 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.