Использование async и await

Changelog

Добавлено в версии 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)

Подключаемые представления на основе классов также поддерживают обработчики, реализованные в виде короутинов. Это относится к методу dispatch_request() в представлениях, которые наследуются от класса flask.views.View, а также ко всем обработчикам методов HTTP в представлениях, которые наследуются от класса flask.views.MethodView.

Использование async в Windows на Python 3.8

В Python 3.8 есть ошибка, связанная с asyncio на Windows. Если вы столкнулись с чем-то вроде ValueError: set_wakeup_fd only works in main thread, пожалуйста, перейдите на Python 3.9.

Использование async с гринлетом

При использовании gevent или eventlet для обслуживания приложения или исправления времени выполнения требуется greenlet>=1.0. При использовании PyPy требуется PyPy>=7.3.7.

Производительность

Для выполнения асинхронных функций требуется цикл событий. Flask, как WSGI-приложение, использует один рабочий для обработки одного цикла запроса/ответа. Когда запрос поступает к асинхронному представлению, Flask запускает цикл событий в потоке, запускает в нем функцию представления, а затем возвращает результат.

Каждый запрос все равно привязывает одного рабочего, даже для асинхронных представлений. Преимуществом является то, что вы можете запускать асинхронный код внутри представления, например, для выполнения нескольких одновременных запросов к базе данных, HTTP-запросов к внешнему API и т. д. Однако количество запросов, которые ваше приложение может обработать за один раз, останется прежним.

Async по своей сути не быстрее, чем синхронизированный код. Async полезен при выполнении параллельных задач, связанных с IO, но, вероятно, не улучшит выполнение задач, связанных с CPU. Традиционные представления Flask по-прежнему подходят для большинства случаев использования, но поддержка асинхронного кода Flask позволяет писать и использовать код, который раньше был невозможен.

Фоновые задачи

Асинхронные функции будут выполняться в цикле событий до тех пор, пока они не завершатся, после чего цикл событий остановится. Это означает, что все дополнительные порожденные задачи, которые не завершились к моменту завершения асинхронной функции, будут отменены. Поэтому вы не можете порождать фоновые задачи, например, через asyncio.create_task.

Если вы хотите использовать фоновые задачи, лучше всего использовать очередь задач для запуска фоновой работы, а не порождать задачи в функции представления. Учитывая это, вы можете порождать задачи asyncio, обслуживая Flask с ASGI-сервером и используя адаптер asgiref WsgiToAsgi, как описано в ASGI. Это работает, поскольку адаптер создает цикл событий, который выполняется непрерывно.

Когда следует использовать кварту вместо кварты

Поддержка асинхронности в Flask менее производительна, чем во фреймворках с асинхронностью, из-за того, как она реализована. Если у вас в основном асинхронная кодовая база, имеет смысл рассмотреть Quart. Quart - это реализация Flask, основанная на стандарте ASGI вместо WSGI. Это позволяет ему обрабатывать множество одновременных запросов, длительные запросы и вебсокеты, не требуя нескольких рабочих процессов или потоков.

Также уже стало возможным запускать Flask с Gevent или Eventlet, чтобы получить многие преимущества асинхронной обработки запросов. Эти библиотеки используют низкоуровневые функции 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

Проверьте журнал изменений расширения, которое вы хотите использовать, чтобы узнать, реализовали ли они поддержку async, или сделайте запрос функции или PR для них.

Другие циклы событий

На данный момент Flask поддерживает только asyncio. Можно переопределить flask.Flask.ensure_sync(), чтобы изменить способ обертывания асинхронных функций для использования другой библиотеки.

Вернуться на верх