【问题标题】:Flask memory spike when processing big json request处理大型 json 请求时 Flask 内存峰值
【发布时间】:2018-10-19 10:40:43
【问题描述】:

我有一个处理 xml 文档的烧瓶应用程序(烧瓶版本 1.0.2)。文档以 json 格式发布,如下所示:

import requests
import sys
import json
with open('big.xml', encoding="utf-8") as f:
    xml_string = f.read()
    print(sys.getsizeof(xml_string) // 1024 // 1024)
    # 283
    gid = "FOO"
    json_data = json.dumps({"file_content": xml_string, "self_id": gid})
    print(sys.getsizeof(json_data) // 1024 // 1024)
    # 305
    result_json = requests.post("http://my_server:8080/api", data=json_data, headers={"Content-Type": "application/json"})

如您所见,xml 文件可能非常大,在本示例中约为 300 MB。

为了简化,我的烧瓶应用程序如下所示:

from flask import Flask, request, jsonify
from memory_profiler import profile

app = Flask(__name__)

@app.route('/api', methods=['POST'])
@profile
def api():
    input_data = request.get_json()
    output_data = {"id": "FOO"}
    response = jsonify(output_data)
    return response

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=8080, debug=True)

在发布请求期间,flask 应用程序的内存使用量飙升至 ~2.8 GB。上面代码中的内存分析与这些数字相去甚远:

Line #    Mem usage    Increment   Line Contents
================================================
     6     27.8 MiB     27.8 MiB   @app.route('/api', methods=['POST'])
     7                             @profile
     8                             def api():
     9    617.3 MiB    589.5 MiB       input_data = request.get_json(request.data)
    10    617.3 MiB      0.0 MiB       output_data = {"id": "FOO"}
    11    617.3 MiB      0.0 MiB       response = jsonify(output_data)
    12    617.3 MiB      0.0 MiB       return response

我错过了什么?是什么导致了这个大的内存峰值以及如何处理它?

【问题讨论】:

  • 仍在追踪为什么,但在request.get_json 之后放置一个断点,如下所示:import pdb; pdb.set_trace()。然后执行此操作:import sys; sys.getsizeof(input_data['file_content'])。您会看到它大约为 1.2GB。随后的请求似乎也会增加它看起来的内存占用,直到它被垃圾收集。
  • @wholevinski 我已经尝试按照您的建议插入断点,sys.getsizeof(input_data['file_content']) 报告的数字与客户端中的 sys.getsizeof(xml_string) 相同(~283 MB)。看起来峰值发生在断点之前,request.get_json 是罪魁祸首。
  • 嗯,是的,我在切换 python3 时看到了与您相同的行为。我只是碰巧在 python2 virtualenv 上尝试让heapy 使用它。但是,是的,看起来get_json 是罪魁祸首。我想它在反序列化时会创建一个新字符串,这将占到大小的 2 倍。
  • 实际上,在切换回 python 3 后,我现在看到内存占用是原始 file_content 大小的 2 倍,而不是一个巨大的峰值。后续请求为我清除,所以你确定它不是累积的吗?你可以重新启动你的 app.py 吗?编辑:看起来它最终会被 GC 清除。
  • @wholevinski 我每次都在运行这个应用程序。我对报告的 ~618 MB 内存使用情况很好,但对 ~2.8 GB 的峰值不满意,由于某种原因在分析时无法跟踪。

标签: python memory flask


【解决方案1】:

如果您不将 xml 包装在 json 结构中并使用标头发送额外信息,我想您可以节省大量内存。

get_jsonjsonify 之类的函数很方便,但没有针对低内存使用进行优化。它们可能会在处理之前复制数据,因此它会多次在内存中。

我认为你做错了什么......烧瓶中的get_json函数具有以下签名:get_json(force=False, silent=False, cache=True) 你不需要将数据放入其中,因为你在请求对象中调用函数。此外,您可能不想将结果缓存在内存中以供多次调用。

试试request.get_json(cache=False),我猜内存使用量会减少几百MB。

此外,我认为 json 函数会占用大量内存:https://blog.ionelmc.ro/2015/11/22/memory-use-and-speed-of-json-parsers/

【讨论】:

  • 感谢您提供删除 requests.data 的提示,这在此处似乎确实没有必要。
  • 至于关闭缓存 - 我之前尝试过。当然,它会稍微减少内存,就像 memory_profiler 报告中的从 ~600 到 ~300 一样,但巨大的内存峰值仍然存在(现在可能不是 ~2.8 GB,而是 2.5 GB)。
  • 我目前正在寻找其他方式来发布请求以绕过这个峰值,但我仍然很好奇为什么会发生这个峰值并且没有反映在分析中。
  • 我猜内存被python中的内部c函数用来解析json字符串..创建一些足够大的缓冲区以容纳所有内容......并在脚本的下一行之前释放它被执行
猜你喜欢
  • 2012-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-07
  • 1970-01-01
  • 2016-12-17
  • 1970-01-01
相关资源
最近更新 更多