使用ValueProvider作为Apache Beam中的路径提取zip内容

时间:2017-09-05 10:11:35

标签: google-cloud-dataflow apache-beam

我有一个代码可以在Google云端存储中提取.ZIP文件的内容。它工作正常,但我需要使用此代码与将在运行时提供的文件路径(“gs://some_bucket/filename.zip”)。当我使用运行时值尝试它时,我得到一个错误:

import numpy as np
import tensorflow as tf

series = tf.placeholder(tf.float32, shape=[None, 5])
series_length = tf.placeholder(tf.int64)

def magic_slice_function(input_x, input_y):
    array = []
    for i in range(len(input_x)):
        temp = [input_x[i][j] for j in range(input_y[i])]
        array.extend(temp)
    return [array]

with tf.Session() as sess:
    input_x = np.array([[1, 2, 3, 0, 0],
                        [2, 3, 0, 0, 0],
                        [1, 0, 0, 0, 0]])

    input_y = np.array([3, 2, 1], dtype=np.int64)

    merged_series =  tf.py_func(magic_slice_function, [series, series_length], tf.float32, name='slice_func')

    out = tf.split(merged_series, input_y)
    print(sess.run(out, feed_dict={series: input_x, series_length: input_y}))

我正在使用的代码是:

[array([ 1.,  2.,  3.], dtype=float32), array([ 2.,  3.], dtype=float32), array([ 1.], dtype=float32)]

UnzipFN类:

Exception in thread "main" java.lang.IllegalArgumentException: unable to serialize org.apache.beam.sdk.io.gcp.bigquery.BigQueryQuerySource@187bc24
    at org.apache.beam.sdk.util.SerializableUtils.serializeToByteArray(SerializableUtils.java:53)
    at org.apache.beam.sdk.util.SerializableUtils.ensureSerializable(SerializableUtils.java:83)
    at org.apache.beam.sdk.io.Read$Bounded.<init>(Read.java:94)
    at org.apache.beam.sdk.io.Read$Bounded.<init>(Read.java:89)
    at org.apache.beam.sdk.io.Read.from(Read.java:48)
    at org.apache.beam.sdk.io.gcp.bigquery.BigQueryIO$Read.expand(BigQueryIO.java:535)
    at org.apache.beam.sdk.io.gcp.bigquery.BigQueryIO$Read.expand(BigQueryIO.java:292)
    at org.apache.beam.sdk.Pipeline.applyInternal(Pipeline.java:482)
    at org.apache.beam.sdk.Pipeline.applyTransform(Pipeline.java:422)
    at org.apache.beam.sdk.values.PBegin.apply(PBegin.java:44)
    at org.apache.beam.sdk.Pipeline.apply(Pipeline.java:164)
    at BeamTest2.StarterPipeline.main(StarterPipeline.java:180)
Caused by: java.io.NotSerializableException: org.apache.beam.sdk.Pipeline
    at java.io.ObjectOutputStream.writeObject0(Unknown Source)
    at java.io.ObjectOutputStream.defaultWriteFields(Unknown Source)
    at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
    at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
    at java.io.ObjectOutputStream.writeObject0(Unknown Source)
    at java.io.ObjectOutputStream.defaultWriteFields(Unknown Source)
    at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
    at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
    at java.io.ObjectOutputStream.writeObject0(Unknown Source)
    at java.io.ObjectOutputStream.defaultWriteFields(Unknown Source)
    at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
    at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
    at java.io.ObjectOutputStream.writeObject0(Unknown Source)
    at java.io.ObjectOutputStream.writeObject(Unknown Source)
    at org.apache.beam.sdk.util.SerializableUtils.serializeToByteArray(SerializableUtils.java:49)
    ... 11 more

如何处理这种情况?

P.S。 - .zip提取代码与BigQueryIO.read()无关。我只是将它用作hack才能读取运行时值。如果您有任何其他建议请告诉我。

感谢。

1 个答案:

答案 0 :(得分:1)

如果我理解正确,您的ValueProvider<String>包含一个文件模式,并且您正在使用GcsUtil.expand()扩展文件模式,并且您希望将函数(UnzipFn)应用于每个文件模式生成的文件名。

目前的代码不会有多种原因:

  • 您正在创建一个BigQueryIO.read().fromQuery(),其中fromQuery()的参数是ValueProvider,它总是返回空字符串(您的NestedValueProvider,在做了一堆之后,始终返回空字符串"")。这将在运行时失败,因为查询不能为空。使用BigQueryIO作为黑客来尝试访问ValueProvider并不是一个好主意 - 请参阅下文。
  • 您正在为函数内部的管道添加步骤,以便从ValueProvider中提取值。在管道运行时,从工作程序调用该函数,以获取提供程序的运行时值。在管道运行时,无法从其工作人员向管道添加步骤。
  • 您正在将Pipeline对象捕获到SerializableFunction闭包中,并且无法序列化,因为Pipeline不是Serializable - 因为没有合法的用例用于序列化Pipeline Java对象:它永远不需要运送给工作者或运行者,它只是主程序中使用的临时构建器对象,用于构造稍后可以调用.run()的东西。另一方面,SerializableFunction 发送给工作人员,以便他们可以评估ValueProvider的当前值。

ValueProvider视为占位符,仅在管道运行时具有值,而不是在构造时具有值 - 例如您可以在provider.get()内拨打DoFnNestedValueProvider根本不会改变这一点 - 它只是通过一些简单的转换逻辑包装另一个ValueProvider,并且当你有一个ValueProvider<Something>但需要它作为{{ {1}}。

问题的关键在于您尝试仅在运行时使用值(ValueProvider<SomethingSlightlyDifferent> options.getInputFile())来构建构建时间 - 创建管道步骤{{1} }。从逻辑上讲,在构造时避免ValueProvider不可用是不可能的:Create.of(paths)专门用于表示在施工时尚未提供的值,因此它们是在管道描述中保留为占位符,并仅在管道运行时作为参数提供。您需要提出一个管道结构,其中输入文件是占位符,管道以您想要的方式处理它。

你可以这样做:

ValueProvider

其中ValueProviderp.apply(Create.ofProvider(options.getInputFile(), StringUtf8Coder.of())) .apply(ParDo.of(new ExpandFn())) .apply(...fusion break...) .apply(ParDo.of(new UnzipFn())) ,其为ExpandFn并且为您的DoFn内容,并且对于融合中断,请参阅例如实施String

在Beam 2.2中(你现在可以在HEAD上使用它)你不需要GcsUtil.expand() - 已经存在一个可以扩展filepatterns的转换等等(例如它可以逐步扩展文件模式并继续观察对于匹配它的新文件,在流式传输管道中)。所以你可以更简洁地写出来:

JdbcIO.java