python-2.7 - 如何在AWS Lambda函数中查找S3文件而不先下载它?

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

我是 AWS Lambda 新手,正在尝试创建一个将由 S3 put 事件调用的 lambda 函数,对传入数据应用一些业务逻辑,然后加载到目标中。

例如,在源 s3 存储桶中创建的新文件 (contactemail.json) 包含 2 个组件:电子邮件和域名。同一 s3 存储桶中还有另一个持久文件 (lkp.json),其中包含所有免费电子邮件域的列表(例如 gmail.com)。 lambda 函数读取 contactemail.json 文件,根据域查找 lkp.json 文件。如果 contactemail.json 中的域存在于 lkp.json 中,请将整个电子邮件地址放入 contactemail.json 文件中的新组件(newdomain)中,然后将输出上传到目标 s3 存储桶。

以下是我的代码。它完成了工作,但是,正如您所看到的,我在执行查找之前使用 s3_client.download_file 下载 lkp.json 文件。

我担心的是,如果查找文件太大,下载过程可能会花费太长时间并导致 lambda 函数超时。

是否有更好/更智能的方法来进行查找,而无需将查找文件从 s3 下载到 lambda?

from __future__ import print_function
import boto3
import os
import sys
import uuid
import json

s3_client = boto3.client('s3')

def handler(event, context):

#get source details from event    
    for record in event['Records']:
        sourcebucket = record['s3']['bucket']['name']
        sourcekey = record['s3']['object']['key'] 
        sourcefilename = sourcekey[sourcekey.rfind('/')+1:]
        lookupkey = 'json/contact/lkp/lkp.json' 
        lookupfilename = 'lkp.json'         

#set target based on source value         
        targetbucket = sourcebucket + 'resized'
        targetkey = sourcekey         
        targetfilename = sourcefilename

#set download and upload path in lambda
        download_path = '/tmp/{}'.format(uuid.uuid4())+sourcefilename
        download_path_lkp = '/tmp/{}'.format(uuid.uuid4())+lookupfilename
        upload_path = '/tmp/{}'.format(uuid.uuid4())+targetfilename

#download source and lookup        
        s3_client.download_file(sourcebucket, sourcekey, download_path)
        s3_client.download_file(sourcebucket, lookupkey, download_path_lkp)

    #if not os.path.exists(upload_path):
       # open(upload_path, 'w').close()

        targetfile = open(upload_path, 'w')
        sourcefile = json.loads(open(download_path).read())
        lookupfile = json.loads(open(download_path_lkp).read())

        lookuplist = []

        for row in lookupfile:
            lookuplist.append(row["domain"])

        targetfile.write('[')
        firstrow = True
        for row in sourcefile:
            email = row["email"]
            emaildomain = email[email.rfind('@')+1:]
            if (emaildomain in lookuplist):
                row["newdomain"]=email
            else:
                row["newdomain"]=emaildomain        
            if (firstrow==False):
                targetfile.write(',\n')
            else:
                firstrow=False     
            json.dump(row, targetfile)
        targetfile.write(']')

        targetfile.close()

#upload to target        
        s3_client.upload_file(upload_path, targetbucket, targetkey)

最佳答案

简单地说,S3 不是用于此目的的正确服务。

  • 如果不下载存储在 S3 中的对象,就无法查看其内部。1

  • 对象是 S3 中的原子实体——S3 无法理解任何比对象更小的东西,例如对象内部的“记录”。

  • 也不可能将数据附加到 S3 中的对象。您必须下载、修改它,然后再次上传,如果多个进程并行尝试执行此操作,则至少有一个进程会默默地丢失数据,因为无法锁定 S3 对象FOR UPDATE (一点 SQL 术语)。第二个进程读取原始对象,对其进行修改,然后继续覆盖第一个进程在第二个进程读取该对象后立即保存的更改。

作为一个“跳出框框思考”的人,我将是第一个断言 S3 作为简单、敷衍的 NoSQL 数据库的有效用例 - 毕竟它是,具有无限存储和按键快速查找的键/值存储......但它适合此角色的应用程序是有限的。这不是它的设计目的。

在这种情况下,不同的架构似乎会为您提供更好的服务...不过,如果您将 lambda 函数连接到 VPC 并为 S3 创建 VPC 终端节点或使用 NAT 实例(不是 NAT 网关,有带宽费用),您可以花费 0.04 美元进行 100,000 次下载,因此根据您的规模,重复下载文件可能不是最糟糕的事情......但是您将浪费大量可计费的 lambda 毫秒来重复解析相同的文件文件并扫描它,正如您所知,随着应用程序的增长,这只会变得更慢。看起来 RDS、DynamoDB 或 SimpleDB 可能更适合这里。

您还可以将内容或至少特定的查找结果缓存在内存中、“处理程序”范围之外的对象中......对吗? (不是 python 人,但似乎是合理的)。 Lambda 有时会重用相同的进程,具体取决于工作负载和调用频率。


是的,您可以在不下载整个对象的情况下进行字节范围读取,但这在这里不适用,因为我们需要扫描,而不是查找。

关于python-2.7 - 如何在AWS Lambda函数中查找S3文件而不先下载它?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37402208/

相关文章:

amazon-web-services - 如何与 Elastic Beanstalk 多容器环境共享 AWS EC2 实例

amazon-web-services - 使用 Terraform 进行 S3 跨区域复制

根据页面不显示 CSS

amazon-s3 - 将 100 万个图像文件移动到 Amazon S3

python - 如何在 Python 中以相同顺序将一个列表中的所有整数添加到另一个长度不同的列表中的整数?

windows - 如何在 Windows 上通过 bash 访问 AWS SAM-CLI?

python - 使用 Python 检索 Twitter 数据时出现 Unicode 解码错误

amazon-web-services - python获取本地时区?

c - Python C API - 线程安全吗?

python - 如何使用键盘中的输入关键字将光标从一行编辑移动到另一行编辑