AppEngine 能否*拉入*超过 32MB 的数据?

Can AppEngine *pull* in data over 32MB?

我正在写一个 API 需要摄取高清视频(至少 100MB)。我只能通过 HTTP XML 提要访问视频,所以我只能在获得视频的 URL 后 拉取 视频(使用 GET)。计划将视频存储在 GCS 中。

但在我 upload/write 到 GCS 之前,我 运行 进入了 AppEngine 中每个请求 32MB 的限制。

是否有解决这两个限制的 GAE 方法:

  1. 需要是 AppEngine 可以发起的 GET
  2. 需要能够将数据导入 GCS

我知道 Amazon S3,如果我必须在 Google 云产品之外,但我不知道它是否可以配置为提取大数据。

谢谢。

根据 Paul Collingwood 的建议,我提出了以下建议。

我决定不将块写入 GCS 然后再将它们拼接在一起。相反,我选择全部在内存中完成,但我可能会根据资源成本更改它(必须 运行 F4@512MB 以避免超过 F2 的 256MB 软限制)。

def get(self):
    # Work with GAE's 32MB-per-request limit, set to 30MB to stay under
    RANGE = 30*(1024**2)

    url = self.request.get('url')
    request = urllib2.Request(url)

    request.get_method = lambda: 'HEAD'
    response = urllib2.urlopen(request)
    info = response.info()
    logging.debug('Downloading {}B video'.format(info.get('Content-length')))

    request.get_method = lambda: 'GET'
    _buffer = ''
    start = 0
    while True:
        end = start + RANGE
        request.headers['Range'] = 'bytes={}-{}'.format(start, end)
        logging.debug('Buffering bytes {} to {}'.format(start, end))
        _bytes = urllib2.urlopen(request, timeout=60).read()
        _buffer += _bytes
        logging.info('Buffered bytes {} to {}'.format(start, end))

        # If there are less bytes than requested then all bytes
        # have been received, break to avoid an HTTP 416
        if len(_bytes) < (end - start):
            break

        start += (RANGE + 1)

    filename = '/MY-BUCKET/video/test_large.mp4'
    with gcs.open(filename, 'w', content_type='video/mp4') as f:
        f.write(_buffer)
    logging.info('Wrote {}B video to GCS'.format(len(_buffer)))

在日志中看起来像这样:

DEBUG    2015-05-01 02:02:00,947 video.py:27] Buffering bytes 0 to 31457280
INFO     2015-05-01 02:02:11,625 video.py:30] Buffered bytes 0 to 31457280
DEBUG    2015-05-01 02:02:11,625 video.py:27] Buffering bytes 31457281 to 62914561
INFO     2015-05-01 02:02:22,768 video.py:30] Buffered bytes 31457281 to 62914561
DEBUG    2015-05-01 02:02:22,768 video.py:27] Buffering bytes 62914562 to 94371842
INFO     2015-05-01 02:02:32,920 video.py:30] Buffered bytes 62914562 to 94371842
...
Writing to GCS
...
INFO     2015-05-01 02:02:41,274 video.py:42] Wrote 89635441B video to GCS

更新,6/May/15

按照 Kekito 的建议,我将 GCS 写入移动到循环中,在整个持续时间内保持文件句柄打开。

url = self.request.get('url')

request = urllib2.Request(url)
request.get_method = lambda: 'HEAD'
response = urllib2.urlopen(request)
info = response.info()
content_length = int(info.get('Content-length'))
logging.debug('Downloading {}B video'.format(content_length))

del(info)
del(response)
del(request)

request = urllib2.Request(url)
start = 0
filename = '/MY-BUCKET/video/test_large.mp4'
f = gcs.open(filename, 'w', content_type='video/mp4')
while True:
    end = start + RANGE
    request.headers['Range'] = 'bytes={}-{}'.format(start, end)

    f.write(urllib2.urlopen(request, timeout=60).read())

    if end >= content_length:
        break

    start = end + 1

f.close()

按照建议here,我使用top监控GAE本地开发服务器Python进程运行,开始上传,记录内存下载和上传周期之间的足迹。

我还尝试更改一次处理的块大小:将块大小从 30 MB 降低到 20 MB,最大内存使用量减少了约 50 MB。在下图中,正在摄取一个 560 MB 的文件,我正在尝试跟踪:

  1. GC:内存占用最低,而urlopen()G设置C大量数据
  2. WC:内存使用峰值,而f.write()W,即C大块头到 GCS

20-MB-Chunk-Test 的最大值为 230 MB,而 30-MB-Chunk-Test 的最大值为 281 MB。因此,我可以 运行 一个只有 256 MB 的实例,但在 512 MB 时 运行 可能会感觉更好。我也可以尝试更小的块大小。