关于python:为什么flask \\的jsonify方法慢?

Why is flask's jsonify method slow?

我正在用烧瓶编写返回json的API。每个烧瓶函数的形式为

1
2
3
4
5
from flask import jsonify
@app.route('/getdata')
def get_data():
    data = load_data_as_dict()
    return jsonify(data)

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

1
2
3
4
5
6
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"

...该功能在大约0.05秒内完成。

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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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), '\
'
),
        mimetype=current_app.config['JSONIFY_MIMETYPE']
    )

dumps如果模块可用,则包装simplejson.dumps,否则使用json.dumps


jsonify()仅包装json.dumps()。但是,根据Flask应用程序的配置和所使用的Flask版本,它可能会将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上的json.dumps()的运行时间从7秒增加到31秒专业版:

1
2
3
4
5
6
7
8
>>> 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

从Flask 1.0开始,如果出现以下情况之一,则会发生这种昂贵的精美印刷:

  • 您已在应用程序的配置中将JSONIFY_PRETTYPRINT_REGULAR显式设置为True(默认为False),或者
  • 您正在以调试模式运行应用

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

如果您正在使用Flask> = 1.0,并且(甚至在调试模式下)需要禁用漂亮打印,则始终可以通过复制并粘贴内置的jsonify来实现自己的jsonify的定义并删除所有漂亮的打印逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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) + '\
'
,
        mimetype=current_app.config['JSONIFY_MIMETYPE']
    )

如果您使用的是Flask 1.0之前的版本,则如果同时出现以下两种情况,则会进行漂亮的打印:

  • 您尚未在应用程序的配置中将JSONIFY_PRETTYPRINT_REGULAR设置为False(默认为True),并且
  • 当前请求不是XHR请求

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

1
app.config['JSONIFY_PRETTYPRINT_REGULAR'] = False

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


我花了一些时间才弄清楚,但是Flask jsonify在编码器上设置了sort_keys参数,看来它默认为True

添加:

1
JSON_SORT_KEYS = False

该配置使我可以将大型JSON结构的速度提高7倍。