gpt4 book ai didi

每次 API 调用导致的 Flask App Memory Leak

转载 作者:行者123 更新时间:2023-12-03 07:35:26 25 4
gpt4 key购买 nike

我的 Flask API 有一个小的内存泄漏,通过多次 API 调用会导致我的应用程序达到它的内存限制并崩溃。我一直在试图弄清楚为什么到目前为止没有成功释放一些内存,我相信我确实知道来源。我很感激任何帮助!

不幸的是,我无法共享代码,只能用英语描述它,我的 flask 应用程序为用户提供了一个 API 端点来执行以下操作(一次调用):

  • 根据提供的 ID 从 MongoDB 中提取一些数据。
  • 根据返回的内容,使用 python-docx 构建一个文档对象。库并将其保存到磁盘。
  • 最后,我将保存到磁盘的内容上传到 S3 存储桶,然后删除磁盘上的内容。

  • 据我所知,使用 memory_profiler library我看到内存使用最多的两个区域是 Document 对象的初始化和连接/保存到 S3(分别为 7MB 和 4.8MB)。

    我正在监视我的 Python 进程的内存使用情况是让 psutils 打印出在某些关键点使用的 rss 内存(下面的示例代码)。
    process = psutil.Process(os.getpid())
    mem0 = process.memory_info().rss
    print('Memory Usage After Action',mem0/(1024**2),'MB')

    ## Perform some action

    mem1 = process.memory_info().rss
    print('Memory Usage After Action',mem1/(1024**2),'MB')
    print('Memory Increase After Action',(mem1-mem0)/(1024**2),'MB')

    提供的控制台图像是在我在本地托管应用程序时调用了三次之后。

    console image provided

    令人担忧的是,每个连续的 API 调用似乎都开始于或高于最后一次调用留下的内存使用量,并继续添加到它。 该应用程序从 93MB 开始(见黄色突出显示),但在第一次调用后它以 103.79MB 结束,第二次以 103.87MB 开始并以 105.39MB 结束,第三次以 105.46Mb 开始并以 106MB 结束。使用量在减少,但在 100 次调用后,我仍然看到内存使用量增加。红线和蓝线显示了 API 调用期间各个点的内存变化。红线是在文档构建之后,蓝线是在 S3 上传之后。

    请注意,我的测试程序每次都使用相同的参数调用 API。

    除其他外,我已经测试了以下内容:
  • gc.collect()。
  • 使用“del”显式删除变量/对象引用。
  • 确保关闭 mongo 连接(因为我使用 IBM_Botos3 library 用于 S3 连接,我不知道是否有办法显式关闭此连接)。
  • 没有使用每个 API 调用保存的全局变量(app 是唯一的全局变量)。

  • 我知道,因为我无法提供代码,所以这里可能没什么可做的,但如果没有想法,我想知道是否有处理 flask 内存使用的最佳实践方法或在 flask 函数返回某些内容后清除内存的方法.现在我的 flask 函数是相对标准的 Python 函数(所以我希望这个函数中的局部变量之后会被垃圾收集)。

    我正在使用 Python 3.6、Flask 0.11.1 和 pymongo 3.6.1,我的测试现在在 Windows 7 机器上,但我的 IBM 云服务器遇到了同样的问题。

    最佳答案

    重要的提示
    既然问了这个问题,Sanked Patel gave a talk at PyCon India 2019关于如何修复 Flask 中的内存泄漏。这是他的策略的总结。
    最小的例子
    假设您有一个简单的无状态 Flask 应用程序,只有一个名为“foo”的端点。请注意,其他端点“内存”和“快照”不是原始应用程序的一部分。我们稍后需要它们来查找内存泄漏。

    import gc
    import os
    import tracemalloc

    import psutil
    from flask import Flask

    app = Flask(__name__)
    global_var = []
    process = psutil.Process(os.getpid())
    tracemalloc.start()
    s = None


    def _get_foo():
    global global_var
    global_var.append([1, "a", 3, True] * 10000) # This is our (amplified) memory leak
    return {'foo': True}


    @app.route('/foo')
    def get_foo():
    gc.collect() # does not help
    return _get_foo()


    @app.route('/memory')
    def print_memory():
    return {'memory': process.memory_info().rss}


    @app.route("/snapshot")
    def snap():
    global s
    if not s:
    s = tracemalloc.take_snapshot()
    return "taken snapshot\n"
    else:
    lines = []
    top_stats = tracemalloc.take_snapshot().compare_to(s, 'lineno')
    for stat in top_stats[:5]:
    lines.append(str(stat))
    return "\n".join(lines)


    if __name__ == '__main__':
    app.run()
    内存泄漏在第 17 行并由注释指示。不幸的是,这种情况很少发生。 ;)
    如您所见,我试图通过手动调用垃圾收集来修复内存泄漏,即 gc.collect() , 在端点 'foo' 返回值之前。但这并不能解决问题。
    寻找内存泄漏
    为了查明是否存在内存泄漏,我们多次调用端点“foo”并测量 API 调用前后的内存使用情况。另外,我们将采取两个 tracemalloc快照。 tracemalloc是一个调试工具,用于跟踪 Python 分配的内存块。如果您使用 Python 3.4+,它在标准库中。
    以下脚本应阐明该策略:
        import requests

    # Warm up, so you don't measure flask internal memory usage
    for _ in range(10):
    requests.get('http://127.0.0.1:5000/foo')

    # Memory usage before API calls
    resp = requests.get('http://127.0.0.1:5000/memory')
    print(f'Memory before API call {int(resp.json().get("memory"))}')

    # Take first memory usage snapshot
    resp = requests.get('http://127.0.0.1:5000/snapshot')

    # Start some API Calls
    for _ in range(50):
    requests.get('http://127.0.0.1:5000/foo')

    # Memory usage after
    resp = requests.get('http://127.0.0.1:5000/memory')
    print(f'Memory after API call: {int(resp.json().get("memory"))}')

    # Take 2nd snapshot and print result
    resp = requests.get('http://127.0.0.1:5000/snapshot')
    pprint(resp.text)
    输出:
    Memory before API call 35328000
    Memory after API call: 52076544
    ('.../stackoverflow/flask_memory_leak.py:17: '
    'size=18.3 MiB (+15.3 MiB), count=124 (+100), average=151 KiB\n'
    '...\\lib\\tracemalloc.py:387: '
    'size=536 B (+536 B), count=3 (+3), average=179 B\n'
    '...\\lib\\site-packages\\werkzeug\\wrappers\\base_response.py:190: '
    'size=512 B (+512 B), count=1 (+1), average=512 B\n'
    '...\\lib\\tracemalloc.py:524: '
    'size=504 B (+504 B), count=2 (+2), average=252 B\n'
    '...\\lib\\site-packages\\werkzeug\\datastructures.py:1140: '
    'size=480 B (+480 B), count=1 (+1), average=480 B')
    在 API 调用之前和之后的内存使用存在很大差异,即内存泄漏。快照端点的第二次调用返回五个最高的内存使用差异。第一个结果在第 17 行正确定位了内存泄漏。
    如果内存泄漏隐藏在代码的更深处,您可能需要调整策略。我只介绍了 tracemalloc 的功能。但是有了这个策略,你就有了一个很好的起点。

    关于每次 API 调用导致的 Flask App Memory Leak,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49991234/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com