【问题标题】:AWS Lambda (Python) Fails to unzip and store files in S3AWS Lambda (Python) 无法在 S3 中解压缩和存储文件
【发布时间】:2018-10-22 02:03:44
【问题描述】:

Project 当前维护 S3 存储桶,该存储桶包含 1.5 GB 的大 zip 大小,其中包含 .xpt 和 .sas7dbat 文件。解压后的文件大小为 20 GB。

尝试解压缩文件并将相同的文件夹结构推送到 S3

以下代码适用于小型 zip 文件,但不适用于大型 Zip 文件 (1.5GB):

for obj in bucket.objects.all():
    #file_name = os.path.abspath(obj.key) # get full path of files
    key = urlparse(obj.key.encode('utf8'))
    obj = client.get_object(Bucket='my-zip-bucket', Key=obj.key)

    with io.BytesIO(obj["Body"].read()) as tf:
        # rewind the file
        tf.seek(0)

        with zipfile.ZipFile(tf, mode='r') as zipf:
            for file in zipf.infolist():
                fileName = file.filename
                putFile = client.put_object(Bucket='my-un-zip-bucket-', Key=fileName, Body=zipf.read(file))
                putObjects.append(putFile)

错误:内存大小:3008 MB 使用的最大内存:3008 MB

我想验证:

  1. AWS-Lambda 不是大文件的合适解决方案?
  2. 我应该使用不同的库/方法而不是读取内存中的所有内容

【问题讨论】:

    标签: python amazon-web-services amazon-s3 aws-lambda


    【解决方案1】:

    有一个使用 AWS Glue 的无服务器解决方案!(我差点想死了)

    此解决方案分为两部分:

    1. 上传 ZIP 文件时由 S3 触发并创建 GlueJobRun 的 lambda 函数 - 将 S3 对象键作为参数传递给 Glue。
    2. 解压缩文件(在内存中!)并上传回 S3 的胶水作业。

    请参阅下面的代码,该代码解压缩 ZIP 文件并将内容放回同一存储桶(可配置)。

    如果有帮助请点赞:)

    调用名为 YourGlueJob 的 Glue 作业的 Lambda 脚本 (python3)

    import boto3
    import urllib.parse
    
    glue = boto3.client('glue')
    
    def lambda_handler(event, context):
        bucket = event['Records'][0]['s3']['bucket']['name']
        key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8')
        print(key)    
    try:
        newJobRun = glue.start_job_run(
            JobName = 'YourGlueJob',
            Arguments = {
                '--bucket':bucket,
                '--key':key,
            }
            )
        print("Successfully created unzip job")    
        return key  
    except Exception as e:
        print(e)
        print('Error starting unzip job for' + key)
        raise e         
    

    用于解压缩文件的 AWS Glue 作业脚本

    import sys
    from awsglue.transforms import *
    from awsglue.utils import getResolvedOptions
    from pyspark.context import SparkContext
    from awsglue.context import GlueContext
    from awsglue.job import Job
    
    ## @params: [JOB_NAME]
    args = getResolvedOptions(sys.argv, ['JOB_NAME','bucket','key'],)
    
    sc = SparkContext()
    glueContext = GlueContext(sc)
    spark = glueContext.spark_session
    job = Job(glueContext)
    job.init(args['JOB_NAME'], args)
    
    import boto3
    import zipfile
    import io
    from contextlib import closing
    
    s3 = boto3.client('s3')
    s3r = boto3.resource('s3')
    
    bucket = args["bucket"]
    key = args["key"]
    
    obj = s3r.Object(
        bucket_name=bucket,
        key=key
    )
    
    buffer = io.BytesIO(obj.get()["Body"].read())
    z = zipfile.ZipFile(buffer)
    list = z.namelist()
    for filerr in list:
        print(filerr)
        y=z.open(filerr)
        arcname = key + filerr
        x = io.BytesIO(y.read())
        s3.upload_fileobj(x, bucket, arcname)
        y.close()
    print(list)
    
    
    job.commit()
    

    【讨论】:

    • 你能解释一下如何使用胶水作业吗?
    • @bruvio 胶水作业应该和上面的代码一样。在这种情况下,作业由 S3 事件触发器创建,该触发器将 S3 存储桶和密钥名称传递给函数。然后我使用 python boto 库去获取文件并处理它。
    【解决方案2】:

    AWS Lambda Limits link中所述:

    但是 AWS Lambda 施加了一些限制,例如,您的部署包的大小或您的 Lambda 函数每次调用分配的内存量。

    在这里,您遇到的问题是因为需要“每次调用分配的内存量 Lambda 函数”。不幸的是,Lambda 不是这种情况下的适用解决方案。您需要使用 EC2 方法。

    当您的整体内存要求很高时,我认为 Lambda 不是很好的解决方案。我不是关于指定的文件类型如何工作,但通常读取/处理大文件使用分块方法来避免大内存需求。分块方法是否有效取决于您的业务需求。

    【讨论】:

      【解决方案3】:

      感谢 @Ganondorfz 的无服务器解决方案。

      我尝试了类似的方法并使用 Go lambda 进行解压缩。认为在开始研究此问题时,可能值得注意的是我最初不太清楚的地方。

      回答问题:

      1. AWS-Lambda 不是大文件的合适解决方案?

      不适用于 zip 文件解压缩。 Zips 是一种存档格式,其末尾有文件索引,AFAICT 所有实用程序和库都希望在内部寻找给定的文件位置,因此受到 lambda 的磁盘和内存限制。我想可以写一些东西来跳转到 S3 对象中的范围,但这将是一个非常复杂的解决方案 - 我还没有看到用于此的实用程序(尽管我可能是错的),并且使用 EC2 实例或容器要简单得多用适当的资源来实现解压。

      但是可以流式传输 gzip 文件,因此在此处使用 lambda 进行大文件解压缩。

      也可以与用例相反 - 将文件从 S3 读取流式传输到写入 S3 的 zip 中。

      1. 我应该使用不同的库/方法而不是读取内存中的所有内容

      我通过 Go 运行时取得了更大的成功/看到了更好的资源利用率,但如上所述,我不相信 lambda 本身适用于这个用例。

      参考:https://dev.to/flowup/using-io-reader-io-writer-in-go-to-stream-data-3i7b

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-11-27
        • 2019-05-03
        • 1970-01-01
        • 1970-01-01
        • 2018-11-27
        • 1970-01-01
        • 2018-11-02
        • 2021-02-12
        相关资源
        最近更新 更多