为什么烧瓶的jsonify方法很慢?

die*_*o_c 15 python api performance json flask

我正在烧瓶中写一个返回json的API.每个烧瓶功能都是这种形式

from flask import jsonify
@app.route('/getdata')
def get_data():
    data = load_data_as_dict()
    return jsonify(data)
Run Code Online (Sandbox Code Playgroud)

如果我返回大量数据,则对此函数的调用大约需要1.7秒.但是,如果我这样做:

from flask import Response
@app.route('/getdata')
def get_data():
    data = load_data_as_dict()
    data_as_str = json.dumps(data)
    return Response(response=data_as_str, status=200, mimetype="application/json"
Run Code Online (Sandbox Code Playgroud)

...该功能在.05秒左右完成.

谁能告诉我为什么jsonify这么慢?返回原始Flask响应有什么问题吗?

kar*_*daj 11

我的猜测是:它与缩进和制作prettyjson转储有很大关系.这是方法定义(我删除了注释以节省空间,完整代码可以在这里找到):

def jsonify(*args, **kwargs):
    indent = None
    separators = (',', ':')

    if current_app.config['JSONIFY_PRETTYPRINT_REGULAR'] and not request.is_xhr:
        indent = 2
        separators = (', ', ': ')

    if args and kwargs:
        raise TypeError('jsonify() behavior undefined when passed both args and kwargs')
    elif len(args) == 1:  # single args are passed directly to dumps()
        data = args[0]
    else:
        data = args or kwargs

    return current_app.response_class(
        (dumps(data, indent=indent, separators=separators), '\n'),
        mimetype=current_app.config['JSONIFY_MIMETYPE']
    )
Run Code Online (Sandbox Code Playgroud)

dumpssimplejson.dumps如果模块可用则换行,否则使用json.dumps.


Mic*_*lvv 6

我花了一段时间才弄清楚,但 Flask在编码器上jsonify设置了sort_keys参数,并且它似乎默认为True.

添加:

JSON_SORT_KEYS = False
Run Code Online (Sandbox Code Playgroud)

对于较大的 JSON 结构,该配置使我的速度提高了 7 倍。

  • 请小心这一点,因为它可能会由于相同数据的不同排序而导致大量不必要的缓存未命中。 (2认同)

Mar*_*ery 5

jsonify()包好了json.dumps()。然而,根据您的水壶应用程序的配置和瓶版本,您正在使用,它可以传递indent=2separators=(', ', ': ')json.dumps。(如果您不熟悉这些参数,请参阅https://docs.python.org/3/library/json.html上的精美印刷文档)。

通过这些论点会json.dumps大大减慢速度。使用https://github.com/zemirco/sf-city-lots-json中 181MB的citylots.json文件作为示例数据,这些漂亮的打印参数将MacBook Pro的运行时间从7秒增加到31秒:json.dumps()

>>> import time 
>>> import json
>>> citylots = json.load(open('citylots.json'))
>>> start = time.time(); x = json.dumps(citylots); print(time.time() - start)
7.165302753448486
>>> x = None
>>> start = time.time(); x = json.dumps(citylots, indent=2, separators=(', ', ': ')); print(time.time() - start)
31.19125771522522
Run Code Online (Sandbox Code Playgroud)

从Flask 1.0开始,如果发生以下两种情况,就会发生这种昂贵的漂亮打印:

  • 您已在应用程序的配置中(默认情况下)明确设置JSONIFY_PRETTYPRINT_REGULAR为,或者TrueFalse
  • 您正在以调试模式运行应用

(您可以在https://github.com/pallets/flask/blob/1.0.2/flask/json/__init__.py#L309的Flask 1.0.2代码中查看这些条件。)

如果您使用Flask> = 1.0,并且(在调试模式下)需要禁用漂亮打印(可能很特殊),则始终可以jsonify通过复制和粘贴内置jsonify的定义并删除所有漂亮的-打印逻辑:

from flask import current_app
from json import dumps

def jsonify(*args, **kwargs):
    if args and kwargs:
        raise TypeError('jsonify() behavior undefined when passed both args and kwargs')
    elif len(args) == 1:  # single args are passed directly to dumps()
        data = args[0]
    else:
        data = args or kwargs

    return current_app.response_class(
        dumps(data) + '\n',
        mimetype=current_app.config['JSONIFY_MIMETYPE']
    )
Run Code Online (Sandbox Code Playgroud)

如果你在一个版本瓶之前到1.0,那么漂亮的印刷,而不是发生什么,如果两个:

  • 尚未在应用程序的配置中(默认情况下)显式设置JSONIFY_PRETTYPRINT_REGULAR为,并且FalseTrue
  • 当前请求不是XHR请求

在那些较旧的版本中,不需要重新定义jsonify以消除漂亮的印刷,因为您可以执行以下操作:

app.config['JSONIFY_PRETTYPRINT_REGULAR'] = False
Run Code Online (Sandbox Code Playgroud)

(或者,如果您使用的是Flask的1.0之前版本,并且只想禁用生产环境中的漂亮打印,则无需更改代码;只需升级到最新版本的Flask。)