从Google App Engine应用运行Google Dataflow管道?

时间:2015-12-07 10:04:23

标签: google-bigquery google-cloud-platform google-cloud-dataflow

我正在使用DataflowPipelineRunner创建数据流作业。我尝试了以下方案。

  1. 未指定任何machineType
  2. 使用g1小机器
  3. 与n1-highmem-2
  4. 在上述所有场景中,Input是来自GCS的文件,文件非常小(KB大小),输出为Big Query表。

    我在所有场景中都出现了内存错误

    我编译的代码的大小是94mb。我只尝试单词计数示例,它没有读取任何输入(它在作业开始之前失败)。请帮助我理解为什么我会收到此错误。

    注意:我正在使用appengine开始工作。

    注意:相同的代码适用于beta versoin 0.4.150414

    编辑1

    根据答案中的建议尝试了以下内容,

    1. 自动缩放切换到基本缩放
    2. 二手机器类型 B2 ,提供256MB内存
    3. 完成这些配置后,Java堆内存问题就解决了。但它试图将一个罐子上传到超过10Mb的交错位置,因此失败了。

      记录以下异常

      com.google.api.client.http.HttpRequest execute: exception thrown while executing request
      com.google.appengine.api.urlfetch.RequestPayloadTooLargeException: The request to https://www.googleapis.com/upload/storage/v1/b/pwccloudedw-stagging-bucket/o?name=appengine-api-L4wtoWwoElWmstI1Ia93cg.jar&uploadType=resumable&upload_id=AEnB2Uo6HCfw6Usa3aXlcOzg0g3RawrvuAxWuOUtQxwQdxoyA0cf22LKqno0Gu-hjKGLqXIo8MF2FHR63zTxrSmQ9Yk9HdCdZQ exceeded the 10 MiB limit.
      at com.google.appengine.api.urlfetch.URLFetchServiceImpl.convertApplicationException(URLFetchServiceImpl.java:157)
      at com.google.appengine.api.urlfetch.URLFetchServiceImpl.fetch(URLFetchServiceImpl.java:45)
      at com.google.apphosting.utils.security.urlfetch.URLFetchServiceStreamHandler$Connection.fetchResponse(URLFetchServiceStreamHandler.java:543)
      at com.google.apphosting.utils.security.urlfetch.URLFetchServiceStreamHandler$Connection.getInputStream(URLFetchServiceStreamHandler.java:422)
      at com.google.apphosting.utils.security.urlfetch.URLFetchServiceStreamHandler$Connection.getResponseCode(URLFetchServiceStreamHandler.java:275)
      at com.google.api.client.http.javanet.NetHttpResponse.<init>(NetHttpResponse.java:36)
      at com.google.api.client.http.javanet.NetHttpRequest.execute(NetHttpRequest.java:94)
      at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:965)
      at com.google.api.client.googleapis.media.MediaHttpUploader.executeCurrentRequestWithoutGZip(MediaHttpUploader.java:545)
      at com.google.api.client.googleapis.media.MediaHttpUploader.executeCurrentRequest(MediaHttpUploader.java:562)
      at com.google.api.client.googleapis.media.MediaHttpUploader.resumableUpload(MediaHttpUploader.java:419)
      at com.google.api.client.googleapis.media.MediaHttpUploader.upload(MediaHttpUploader.java:336)
      at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:427)
      at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:352)
      at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:469)
      at com.google.cloud.hadoop.util.AbstractGoogleAsyncWriteChannel$UploadOperation.call(AbstractGoogleAsyncWriteChannel.java:357)
      at java.util.concurrent.FutureTask.run(FutureTask.java:260)
      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1168)
      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:605)
      at com.google.apphosting.runtime.ApiProxyImpl$CurrentRequestThreadFactory$1$1.run(ApiProxyImpl.java:1152)
      at java.security.AccessController.doPrivileged(Native Method)
      at com.google.apphosting.runtime.ApiProxyImpl$CurrentRequestThreadFactory$1.run(ApiProxyImpl.java:1146)
      at java.lang.Thread.run(Thread.java:745)
      at com.google.apphosting.runtime.ApiProxyImpl$CurrentRequestThreadFactory$2$1.run(ApiProxyImpl.java:1195)
      

      我尝试直接上传jar文件 - appengine-api-1.0-sdk-1.9.20.jar ,仍尝试上传此jar appengine-api-L4wtoWwoElWmstI1Ia93cg.jar < / strong>即可。 我不知道它是什么罐子。知道这个罐子是什么的任何想法。

      请帮我解决这个问题。

3 个答案:

答案 0 :(得分:4)

简短的回答是,如果您在Managed VM上使用AppEngine,则不会遇到AppEngine沙箱限制(使用F1 or B1 instance class时的OOM,执行时间限制问题,列入白名单的JRE类)。如果您确实希望在App Engine沙箱中运行,那么您对Dataflow SDK的使用大多符合AppEngine沙箱的限制。下面我将解释常见问题以及人们为遵守AppEngine沙箱限制所做的工作。

Dataflow SDK需要一个AppEngine实例类,该实例类具有足够的内存来执行用户应用程序以构建管道,暂存所有资源,并将作业描述发送到Dataflow服务。通常我们看到用户需要使用内存超过128mb的instance class才能看到OOM错误。

如果已经暂存了应用程序所需的资源,通常构建管道并将其提交到Dataflow服务通常需要不到几秒钟的时间。将JAR和任何其他资源上载到GCS可能需要超过60秒。这可以通过预先将JAR预先暂存到GCS来手动解决(如果Dataflow SDK检测到它们已经存在,则会再次跳过它们)或使用task queue获得10分钟限制(请注意,对于大型应用程序,10分钟可能不足以暂存您的所有资源)。

最后,在AppEngine沙箱环境中,您和所有依赖项仅限于在JRE中使用whitelisted个类,否则您将获得如下异常:

java.lang.SecurityException:
  java.lang.IllegalAccessException: YYY is not allowed on ZZZ
  ...

编辑1

我们在类路径上执行jar内容的哈希,并使用修改后的文件名将它们上传到GCS。 AppEngine运行带有自己的JAR的沙盒环境, appengine-api-L4wtoWwoElWmstI1Ia93cg.jar 指的是 appengine-api.jar ,它是沙盒环境添加的jar。您可以在我们的PackageUtil#getUniqueContentName(...)中看到我们只是在 .jar 之前附加 - $ HASH

我们正在努力解决您看到 RequestPayloadToLarge 的原因,目前建议您设置 filesToStage 选项并过滤掉执行您不需要的jar数据流来解决您面临的问题。您可以看到我们如何构建要使用DataflowPipelineRunner#detectClassPathResourcesToStage(...)暂存的文件。

答案 1 :(得分:1)

我在10MB限制时遇到了同样的问题。我所做的是过滤掉大于该限制的JAR文件(而不是特定文件),然后使用setFilesToStagepingHandler = scheduler.scheduleAtFixedRate([command], 5, 5, TimeUnit.MINUTES); 中设置重命名文件。

所以我刚刚从Dataflow SDK复制了方法DataflowPipelineOptions,并且明显地改变了它:

detectClassPathResourcesToStage

然后当我创建private static final long FILE_BYTES_THRESHOLD = 10 * 1024 * 1024; // 10 MB protected static List<String> detectClassPathResourcesToStage(ClassLoader classLoader) { if (!(classLoader instanceof URLClassLoader)) { String message = String.format("Unable to use ClassLoader to detect classpath elements. " + "Current ClassLoader is %s, only URLClassLoaders are supported.", classLoader); throw new IllegalArgumentException(message); } List<String> files = new ArrayList<>(); for (URL url : ((URLClassLoader) classLoader).getURLs()) { try { File file = new File(url.toURI()); if (file.length() < FILE_BYTES_THRESHOLD) { files.add(file.getAbsolutePath()); } } catch (IllegalArgumentException | URISyntaxException e) { String message = String.format("Unable to convert url (%s) to file.", url); throw new IllegalArgumentException(message, e); } } return files; }

DataflowPipelineOptions

答案 2 :(得分:1)

这是Helder的10MB过滤solution版本,即使在SDK的未来版本中发生更改,也会适应DataflowPipelineOptions的默认文件暂存行为。

它不会复制逻辑,而是将DataflowPipelineOptions的一次性副本传递给DataflowPipelineRunner以查看它将要播放的文件,然后删除任何太大的文件。

请注意,此代码假定您已定义名为PipelineOptions的自定义MyOptions类以及名为java.util.Logger的{​​{1}}字段。

logger