使用 asyncawait

更新日志

在 2.0 版本中添加。

如果 Flask 安装了 async 扩展(pip install flask[async]),则路由、错误处理程序、请求前、请求后和拆卸函数都可以是协程函数。这允许使用 async def 定义视图,并使用 await

@app.route("/get-data")
async def get_data():
    data = await async_db_query(...)
    return jsonify(data)

可插拔的基于类的视图也支持作为协程实现的处理程序。这适用于继承自 flask.views.View 类的视图中的 dispatch_request() 方法,以及继承自 flask.views.MethodView 类的视图中的所有 HTTP 方法处理程序。

async 与 greenlet 结合使用

当使用 gevent 或 eventlet 来服务应用程序或修补运行时时,需要 greenlet>=1.0。当使用 PyPy 时,需要 PyPy>=7.3.7。

性能

异步函数需要事件循环才能运行。Flask 作为 WSGI 应用程序,使用一个工作进程来处理一个请求/响应周期。当请求进入异步视图时,Flask 将在一个线程中启动一个事件循环,在那里运行视图函数,然后返回结果。

即使对于异步视图,每个请求仍然占用一个工作进程。好处是您可以在视图中运行异步代码,例如进行多个并发数据库查询、对外部 API 的 HTTP 请求等。但是,您的应用程序一次可以处理的请求数量将保持不变。

异步本质上并不比同步代码更快。 异步在执行并发 IO 密集型任务时是有益的,但可能不会改善 CPU 密集型任务。传统的 Flask 视图仍然适用于大多数用例,但 Flask 的异步支持使得编写和使用以前无法原生实现的代码成为可能。

后台任务

异步函数将在事件循环中运行直到完成,届时事件循环将停止。这意味着在异步函数完成时任何尚未完成的其他生成任务都将被取消。因此,您不能生成后台任务,例如通过 asyncio.create_task

如果您希望使用后台任务,最好使用任务队列来触发后台工作,而不是在视图函数中生成任务。考虑到这一点,您可以通过使用 ASGI 服务器服务 Flask 并利用 asgiref WsgiToAsgi 适配器来生成 asyncio 任务,如 ASGI 中所述。这是可行的,因为适配器创建了一个持续运行的事件循环。

何时使用 Quart 代替

由于 Flask 异步支持的实现方式,它的性能不如异步优先框架。如果您主要使用异步代码库,那么考虑 Quart 是有意义的。Quart 是基于 ASGI 标准而不是 WSGI 对 Flask 的重新实现。这使得它能够处理许多并发请求、长时间运行的请求和 WebSocket,而无需多个工作进程或线程。

已经可以使用 Gevent 或 Eventlet 运行 Flask,以获得异步请求处理的许多好处。这些库修补了底层的 Python 函数来实现这一点,而 async/ await 和 ASGI 使用标准的、现代的 Python 功能。决定您应该使用 Flask、Quart 还是其他东西最终取决于了解您项目的具体需求。

扩展

早于 Flask 异步支持的 Flask 扩展不期望异步视图。如果它们提供装饰器来向视图添加功能,这些装饰器可能不适用于异步视图,因为它们不会等待该函数或成为可等待的。它们提供的其他函数也不会是可等待的,并且如果在异步视图中调用,则可能是阻塞的。

扩展作者可以通过使用 flask.Flask.ensure_sync() 方法来支持异步函数。例如,如果扩展提供视图函数装饰器,请在调用装饰函数之前添加 ensure_sync

def extension(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        ...  # Extension logic
        return current_app.ensure_sync(func)(*args, **kwargs)

    return wrapper

查看您要使用的扩展的更新日志,以查看它们是否已实现异步支持,或者向它们提出功能请求或 PR。

其他事件循环

目前,Flask 仅支持 asyncio。可以覆盖 flask.Flask.ensure_sync() 以更改异步函数的包装方式,从而使用不同的库。