【问题标题】: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 响应有什么问题吗?

【问题讨论】:

    标签: python json performance api 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 倍。

      【讨论】:

      • 哦,哇,这是我在回答中完全错过的一件大事!我可能会相应地更新我的。
      • 请注意这一点,因为同一数据的不同排序可能会导致大量不必要的缓存未命中。
      猜你喜欢
      • 2013-04-24
      • 1970-01-01
      • 2020-10-21
      • 2016-03-08
      • 2011-05-17
      • 2019-10-02
      • 2010-10-30
      • 2023-04-03
      • 1970-01-01
      相关资源
      最近更新 更多