为啥flask的jsonify方法很慢?

Posted

技术标签:

【中文标题】为啥flask的jsonify方法很慢?【英文标题】:Why is flask's jsonify method slow?为什么flask的jsonify方法很慢? 【发布时间】:2016-10-22 06:20:36 【问题描述】:

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

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

如果我返回大量数据,调用此函数大约需要 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"

...函数在大约 0.05 秒内完成。

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

【问题讨论】:

【参考方案1】:

我的猜测是:它与缩进和制作pretty json 转储有很大关系。下面是方法定义(为了节省空间,我去掉了cmets,完整代码可以在here找到):

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']
    )

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

【讨论】:

确实如此,我试过了。如果你重新定义 jsonify 和 jsut 删除缩进,它会工作得更快。在我的测试中时间从 17s 变成了 0.1s,170 次!【参考方案2】:

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 文件作为示例数据,这些漂亮的打印参数将 json.dumps() 在我的 MacBook Pro 上的运行时间从 7 秒增加到 31 秒:

>>> 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的定义并删除所有漂亮的打印逻辑:

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']
    )

如果您使用的是 Flask1.0 之前的版本,那么如果两者都发生,则会发生漂亮的打印:

还没有在应用的配置中将JSONIFY_PRETTYPRINT_REGULAR 明确设置为False(默认为True),并且 当前请求不是 XHR 请求

在那些旧版本中,永远不需要重新定义 jsonify 来消除漂亮的打印,因为你可以这样做:

app.config['JSONIFY_PRETTYPRINT_REGULAR'] = False

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

【讨论】:

【参考方案3】:

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

添加:

JSON_SORT_KEYS = False

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

【讨论】:

哦,哇,这是我在回答中完全错过的一件大事!我可能会相应地更新我的。 请注意这一点,因为同一数据的不同排序可能会导致大量不必要的缓存未命中。

以上是关于为啥flask的jsonify方法很慢?的主要内容,如果未能解决你的问题,请参考以下文章

为啥使用VBA后,EXCEL响应变得很慢

为啥使用 CollectionViewSource.SortDescriptions 排序很慢?

为啥innerHTML =“”在Firefox中很慢

为啥U盘运行速度很慢

为啥asp.net响应很慢

为啥每次启动电脑都会很慢