AWS Lambda内存使用情况以及python代码中的临时文件

时间:2018-12-22 21:58:20

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

写入临时文件的数据是否有助于AWS lambda中的内存使用?在lambda函数中,我正在将文件流式传输到临时文件。在lambda日志中,我看到使用的最大内存大于下载的文件。奇怪的是,如果快速连续调用了lambda多次,则下载较小文件的调用仍会报告下载较大文件的调用所使用的最大内存。我将并发限制设置为2。

当我在本地运行代码时,我的内存使用量按预期约为20MB。在lambda上,它是180MB,大约是流文件的大小。该代码只是使用python请求库流式下载文件,将文件传输到shutil.copyfileobj()写入tempfile.TemporaryFile(),然后将其通过管道传递到postgres“从stdin复制”。

这使得/ tmp存储看起来像是占用了内存,但没有找到任何提及。 lambda文档中唯一提到/ tmp的地方是512mb的限制。

示例代码:

import sys
import json
import os
import io
import re
import traceback
import shutil
import tempfile

import boto3
import psycopg2
import requests


def handler(event, context):
    try:
        import_data(event["report_id"])
    except Exception as e:
        notify_failed(e, event)
        raise

def import_data(report_id):
    token = get_token()
    conn = psycopg2.connect(POSTGRES_DSN, connect_timeout=30)
    cur = conn.cursor()

    metadata = load_metadata(report_id, token)
    table = ensure_table(metadata, cur, REPLACE_TABLE)
    conn.commit()
    print(f"report {report_id}: downloading")
    with download_report(report_id, token) as f:
        print(f"report {report_id}: importing data")
        with conn, cur:
            cur.copy_expert(f"COPY {table} FROM STDIN WITH CSV HEADER", f)
        print(f"report {report_id}: data import complete")
    conn.close()


def download_report(report_id, token):
    url = f"https://some_url"
    params = {"includeHeader": True}
    headers = {"authorization": f"Bearer {token['access_token']}"}

    with requests.get(url, params=params, headers=headers, stream=True) as r:
        r.raise_for_status()
        tmp = tempfile.TemporaryFile()
        print("streaming contents to temporary file")
        shutil.copyfileobj(r.raw, tmp)
        tmp.seek(0)
        return tmp


if __name__ == "__main__":
    if len(sys.argv) > 1:
        handler({"report_id": sys.argv[1]}, None)

更新: 更改代码以不使用临时文件,而只是将下载直接流式传输到postgres copy命令后,内存使用情况已修复。让我认为/ tmp目录有助于记录的内存使用情况。

2 个答案:

答案 0 :(得分:6)

更新

注意: 为了回答这个问题,尽管我不得不修改用于node8.10的lambda版本,但我使用了Lambdash。 Lambdash是一个简单的小库,可用于从本地终端在lambda上运行shell命令。

AWS Lambdas上的/ tmp目录安装为loop device。您可以通过运行以下命令(按照lambdash的设置说明进行操作)来验证这一点:

./lambdash df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/xvda1       30G  4.0G   26G  14% /
/dev/loop0      526M  872K  514M   1% /tmp
/dev/loop1      6.5M  6.5M     0 100% /var/task

根据https://unix.stackexchange.com/questions/278647/overhead-of-using-loop-mounted-images-under-linux

  

通过循环设备访问的数据必须经过两个文件系统层,每个文件层都进行自己的缓存,因此数据最终会被缓存两次,浪费大量内存(臭名昭著的“双缓存”问题)

但是,我的猜测是/tmp实际上保留在内存中。为了对此进行测试,我运行了以下命令:

./lambdash df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/xvda1       30G  4.0G   26G  14% /
/dev/loop0      526M  1.9M  513M   1% /tmp
/dev/loop1      6.5M  6.5M     0 100% /var/task

./lambdash dd if=/dev/zero of=/tmp/file.txt count=409600 bs=1024
409600+0 records in
409600+0 records out
419430400 bytes (419 MB) copied, 1.39277 s, 301 MB/s

./lambdash df -h
 Filesystem      Size  Used Avail Use% Mounted on
 /dev/xvda1       30G  4.8G   25G  17% /
 /dev/loop2      526M  401M  114M  78% /tmp
 /dev/loop3      6.5M  6.5M     0 100% /var/task

./lambdash df -h
 Filesystem      Size  Used Avail Use% Mounted on
 /dev/xvda1       30G  4.8G   25G  17% /
 /dev/loop2      526M  401M  114M  78% /tmp
 /dev/loop3      6.5M  6.5M     0 100% /var/task

请记住,每次我运行时,都会执行lambda。以下是Lambda的Cloudwatch日志的输出:

  

07:06:30 START RequestId:4143f502-14a6-11e9-bce4-eff8b92bf218   版本:$ LATEST 07:06:30 END RequestId:   4143f502-14a6-11e9-bce4-eff8b92bf218 07:06:30报告RequestId:   4143f502-14a6-11e9-bce4-eff8b92bf218持续时间:3.60 ms计费   持续时间:100 ms内存大小:1536 MB使用的最大内存:30 MB

     

07:06:32 START RequestId:429eca30-14a6-11e9-9b0b-edfabd15c79f   版本:$ LATEST 07:06:34 END RequestId:   429eca30-14a6-11e9-9b0b-edfabd15c79f 07:06:34报告RequestId:   429eca30-14a6-11e9-9b0b-edfabd15c79f持续时间:1396.29 ms已结算   持续时间:1400 ms内存大小:1536 MB使用的最大内存:430 MB

     

07:06:36 START RequestId:44a03f03-14a6-11e9-83cf-f375e336ed87   版本:$ LATEST 07:06:36 END RequestId:   44a03f03-14a6-11e9-83cf-f375e336ed87 07:06:36报告RequestId:   44a03f03-14a6-11e9-83cf-f375e336ed87时长:3.69 ms已结算   持续时间:100 ms内存大小:1536 MB使用的最大内存:431 MB

     

07:06:38 START RequestId:4606381a-14a6-11e9-a32d-2956620824ab   版本:$ LATEST 07:06:38 END RequestId:   4606381a-14a6-11e9-a32d-2956620824ab 07:06:38报告RequestId:   4606381a-14a6-11e9-a32d-2956620824ab持续时间:3.63毫秒   持续时间:100 ms内存大小:1536 MB使用的最大内存:431 MB

发生了什么,什么意思?

lambda被执行了4次。在第一次执行时,我显示了已安装的设备。在第二次执行时,我使用允许的500Mb中的401Mb填充了/tmp目录中的文件。在随后的执行中,我列出了已安装的设备,并显示了它们的可用空间。

第一次执行时的内存利用率为30Mb。后续执行的内存利用率在400Mb范围内。

这确认/tmp利用率实际上确实有助于内存利用率。

原始答案

我的猜测是,您正在观察的是python或lambda容器本身,在写操作期间将文件缓冲在内存中。

根据https://docs.python.org/3/library/functions.html#open

  

buffering是用于设置缓冲策略的可选整数。传递0来关闭缓冲(仅在二进制模式下允许),传递1来选择行缓冲(仅在文本模式下可用),并传递一个大于1的整数以指示固定大小的块缓冲区的字节大小。如果未指定任何缓冲参数,则默认的缓冲策略如下:

     

二进制文件以固定大小的块缓冲;缓冲区的大小是通过试探法来确定底层设备的“块大小”,然后使用io.DEFAULT_BUFFER_SIZE来选择的。在许多系统上,缓冲区的长度通常为4096或8192字节。   “交互式”文本文件(isatty()返回True的文件)使用行缓冲。其他文本文件将上述策略用于二进制文件。

tempfile.TemporaryFile()函数具有关键字参数buffering,该参数基本上直接传递到上述的open调用中。

所以我的猜测是tempfile.TemporaryFile()函数使用默认的open()函数的缓冲设置。您可以尝试使用tempfile.TemporaryFile(buffering=0)之类的功能禁用缓冲,或者尝试使用tempfile.TemporaryFile(buffering=512)显式设置将数据写入文件时将使用的最大内存量。

答案 1 :(得分:2)

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script> <div id="app"> <ol> <li is="list-item" v-for="item in items" v-on:item-event="itemClick"></li> </ol> </div>的使用不计入内存使用。唯一可以关联的情况是将文件内容读取到内存中。