AWS Lambda "errorMessage":“[Errno 26] 文本文件忙”

AWS Lambda "errorMessage": "[Errno 26] Text file busy"

我编写代码并通过 AWS SAM CLI 成功将其上传到 AWS Lambda。它基本上进入了我给的 URL ,并打印了网站的标题。一个非常初级的代码。下面是我的代码:

import os, shutil, uuid, time
from selenium import webdriver

def setup():
    BIN_DIR = "/tmp/bin"
    if not os.path.exists(BIN_DIR):
        print("Creating bin folder")
        os.makedirs(BIN_DIR)

    LIB_DIR = '/tmp/bin/lib'
    if not os.path.exists(LIB_DIR):
        print("Creating lib folder")
        os.makedirs(LIB_DIR)
        
    for filename in ['chromedriver', 'headless-chromium', 'lib/libgconf-2.so.4', 'lib/libORBit-2.so.0']:
        oldfile = f'/opt/{filename}'
        newfile = f'{BIN_DIR}/{filename}'
        shutil.copy2(oldfile, newfile)
        os.chmod(newfile, 0o775)

def init_web_driver():
    setup()
    chrome_options = webdriver.ChromeOptions()
    _tmp_folder = '/tmp/{}'.format(uuid.uuid4())

    if not os.path.exists(_tmp_folder):
        os.makedirs(_tmp_folder)

    if not os.path.exists(_tmp_folder + '/user-data'):
        os.makedirs(_tmp_folder + '/user-data')

    if not os.path.exists(_tmp_folder + '/data-path'):
        os.makedirs(_tmp_folder + '/data-path')

    if not os.path.exists(_tmp_folder + '/cache-dir'):
        os.makedirs(_tmp_folder + '/cache-dir')

    chrome_options.add_argument('--headless')
    chrome_options.add_argument('--no-sandbox')
    chrome_options.add_argument('--disable-gpu')
    chrome_options.add_argument('--window-size=1280x1696')
    chrome_options.add_argument('--user-data-dir={}'.format(_tmp_folder + '/user-data'))
    chrome_options.add_argument('--hide-scrollbars')
    chrome_options.add_argument('--enable-logging')
    chrome_options.add_argument('--log-level=0')
    chrome_options.add_argument('--v=99')
    chrome_options.add_argument('--single-process')
    chrome_options.add_argument('--data-path={}'.format(_tmp_folder + '/data-path'))
    chrome_options.add_argument('--ignore-certificate-errors')
    chrome_options.add_argument('--homedir={}'.format(_tmp_folder))
    chrome_options.add_argument('--disk-cache-dir={}'.format(_tmp_folder + '/cache-dir'))
    chrome_options.add_argument(
        'user-agent=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36')

    chrome_options.binary_location = "/tmp/bin/headless-chromium"

    driver = webdriver.Chrome(chrome_options=chrome_options)
    return driver

def lambda_handler(event, context):
    driver = init_web_driver()
    driver.get("http://www.mjlivesey.co.uk")
    time.sleep(4)
    print(driver.title)

当我第一次点击“测试”按钮时,我可以在输出屏幕中看到“driver.title”。但是,当我在几秒钟后再次单击时,出现以下错误:

{
  "errorMessage": "[Errno 26] Text file busy: '/tmp/bin/chromedriver'",
  "errorType": "OSError",
  "stackTrace": [
    "  File \"/var/task/app.py\", line 61, in lambda_handler\n    driver = init_web_driver()\n",
    "  File \"/var/task/app.py\", line 22, in init_web_driver\n    setup()\n",
    "  File \"/var/task/app.py\", line 18, in setup\n    shutil.copy2(oldfile, newfile)\n",
    "  File \"/var/lang/lib/python3.7/shutil.py\", line 266, in copy2\n    copyfile(src, dst, follow_symlinks=follow_symlinks)\n",
    "  File \"/var/lang/lib/python3.7/shutil.py\", line 121, in copyfile\n    with open(dst, 'wb') as fdst:\n"
  ]
}

如果我等待半小时或更长时间,我可以再次 运行 代码成功。我不明白这里的问题。或许你们能帮我看清重点。

谢谢。

似乎同一个函数的多次调用都触及了相同的文件,而之前的执行在这些文件上仍然有一个活动锁。

要了解此行为,了解 Lambda Execution environment 的实际工作方式很有用。基本上,如果你在短时间内多次执行相同的功能,AWS 会尝试重用相同的执行环境和资源;这样可以节省时间和资源。

只是为了并行,所发生的事情与在本地并行多次执行您的代码是一样的。由于所有进程都在同一个 files/folders 上读取和写入,因此将不可避免地出现竞争条件。

在您的情况下,您应该重构 setup 函数,使其内容在每个执行环境中只执行一次。

此外,您应该注意 /tmp 目录的硬限制为 512MB,超过 512MB 后您的函数将被终止。如果你想保留你的数据 and/or 有更多的余量,你应该考虑研究 attaching EFS to your lambda.

为了补充之前的答案,我使用了相同的代码,我发现这可以删除 tmp 文件夹并启用后续的 lambdas 运行。

def setup():
    BIN_DIR = "/tmp/bin"
    if not os.path.exists(BIN_DIR):
        print("Creating bin folder")
        os.makedirs(BIN_DIR)
    else:
        print("Delete all files in folder")
        for filename in os.listdir(BIN_DIR):
            file_path = os.path.join(BIN_DIR, filename)
            try:
                if os.path.isfile(file_path) or os.path.islink(file_path):
                    os.unlink(file_path)
                elif os.path.isdir(file_path):
                    shutil.rmtree(file_path)
            except Exception as e:
                print('Failed to delete %s. Reason: %s' % (file_path, e))
        print("Deleting bin folder")
        os.rmdir(BIN_DIR)
        print("Creating bin folder")
        os.makedirs(BIN_DIR)

只需添加

driver.quit()

在您的代码末尾