第16章:性能-Flask Web 开发(第2版)

没有人会喜欢运行速度慢的应用程序。长时间等待页面加载会让用户非常不爽,因此在出现问题时尽快检测并纠正性能问题非常重要。在本章中,我们将讨论 Web 应用程序的两个重要性能。

对性能低下的数据库进行记录

随着数据库规模的增长,数据库查询速度会越来越慢,从而导致应用程序的性能退化。优化数据库查询可以像添加更多索引一样简单,也可以像在应用程序和数据库之间添加缓存一样复杂。explain 语句是大多数数据库都提供的语句。显示数据库执行给定查询的步骤,通常会暴露数据库或索引设计中的存在的低性能问题。

但在开始优化查询之前,有必要确定哪些查询是值得优化的查询。在典型的请求过程中,可能会发出几个数据库查询,因此通常很难确定哪些查询是慢速查询。Flask-SQLAlchemy 可以配置成记录请求期间发出的数据库查询的统计信息。在示例16-1中,您可以看到如何使用此功能记录比配置的阈值慢的查询。

# 示例 16-1. app/main/views.py: 对数据库中的慢查询进行报告
from flask_sqlalchemy import get_debug_queries

@main.after_app_request
def after_request(response):
    for query in get_debug_queries():
        if query.duration >= current_app.config['FLASKY_SLOW_DB_QUERY_TIME']:
            current_app.logger.warning(
                '慢查询: %s\n参数: %s\n时长: %fs\n上下文: %s\n' %
                    (query.statement, query.parameters, query.duration,
                     query.context))
    return response

此功能通过 after_app_request 处理器实现。它和 before_app_request 的工作方式类似,只是它在所有请求的视图函数处理器返回后自动调用。 Flask 将响应对象传递给这个处理器,处理器可以按需对其进行修改。

在这个例子中,after_app_request 处理器没有操作响应对象;它只是获取了 Flask-SQLAlchemy 中的查询列表,并使用 Flask 的 app.logger 对其中的慢查询进行记录。在返回响应对象之前,它会把这些记录发送到客户端。

get_debug_queries() 方法会返回本次请求中的所有查询的列表。这些查询提供的信息如下表所示。

名称 说明
statement SQL 语句
parameters SQL 语句中使用的所有参数
start_time 执行该查询的开始时间
end_time 该查询执行完成后的时间
duration 该查询的执行时长,单位为秒
context 标识该查询执行位置的字符串

是否是慢查询,是通过 FLASKY_SLOW_DB_QUERY_TIME 配置的。

get_debug_queries() 函数默认只在调试模式下可用。不幸的是,数据库的性能问题无法在开发环境中真实呈现,因为在开发环境中使用的数据量并不大。基于此原因,需要在生产环境中启用这个选项。示例 16-2 显示了如何在生产环境中启用对数据库查询性能的监控(SQLALCHEMY_RECORD_QUERIES = True),同时还显示了 FLASKY_SLOW_DB_QUERY_TIME 的配置。

# 示例 16-2. config.py: 对慢查询报告进行配置
class Config:
    # ...
    SQLALCHEMY_RECORD_QUERIES = True
    FLASKY_SLOW_DB_QUERY_TIME = 0.5
    # ...

当识别慢查询时,需要将其写到 Flask 应用程序的日志中。所以,需要对日志进行配置,以启用该功能。日志的配置严重依赖应用程序所在宿主机的平台。第17章将显示一些示例。

如果你从 Github 克隆了示例代码,可以使用 git checkout 16a 签出本示例。

源代码分析

性能问题的另一个可能来源是CPU占用率过高,这是由执行大量计算的功能造成的。源代码分析器可用于查找应用程序的最慢部分。分析器监视正在运行的应用程序并记录被调用的函数及其运行时间。然后它会生成一份显示有运行最慢的函数的详细报告。

分析通常只在开发环境中完成。源代码分析器使得应用程序运行速度比平时慢得多,因为它必须实时观察并记录所有正在发生的事情。不建议在生产环境上进行分析,除非使用专门设计用于生产环境中运行的轻量级分析器。

基于 Werkzeug 的 Flask 开发 Web 服务器可以为每个请求启用 Python 分析器。

示例16-3给应用程序增加了一个新的命令行参数,用于给应用程序启用分析器。

# 示例 16-3. flasky.py: 运行带有分析器的应用程序
@app.cli.command()
@click.option('--length', default=25,
              help='Number of functions to include in the profiler report.')
@click.option('--profile-dir', default=None,
              help='Directory where profiler data files are saved.')
def profile(length, profile_dir):
    """Start the application under the code profiler."""
    from werkzeug.contrib.profiler import ProfilerMiddleware
    app.wsgi_app = ProfilerMiddleware(app.wsgi_app, restrictions=[length],
                                      profile_dir=profile_dir)
    app.run(debug=False)

这个命令通过 Werkzeug 的 wsgi_app 属性,将它的 ProfilerMiddleware 附加到应用程序上。每次 Web 服务器向应用程序发送请求时都会调用 WSGI 中间件,并且可以修改请求的处理方式,通过这种方式可以获取到进行分析的数据。

如果你从 Github 克隆了示例代码,可以使用 git checkout 16b 签出本示例。

使用 flask profile 运行这个应用程序的时候,控制台将显示每个请求的分析结果,这个结果包含了最慢的25个函数。--length 参数设定结果中包含的函数的个数。如果设置了 --profile-dir 选项,分析结果将以文件形式保存这个选项指定目录中。

部署应用的准备工作已经完成。下一次将介绍如何部署应用。

本文并非《Flask Web Development(2nd edition)》的全文翻译。而只是在阅读该书时,对自认为重点的内容进行记录以及思考。由于水平有限,本文所述内容难免出现不足或错误。请通过购买正版图书来进行更系统地学习,原书地址。本站将依照本系列文章录制视频教程,届时本文未作记录的部分以及项目实际开发中所需要的知识都会在视频教程中进行补充。

flask Flask Web Development(2nd edition) 2018-03-30 11:04 1529886