Перейти к содержанию

Фоновые задачи и жизненный цикл

На этой странице две вещи: код, который выполняется при старте и остановке приложения (открыть пул БД, закрыть его), и работа, отложенная до момента после отправки ответа (отправить письмо, не заставляя клиента ждать).

Хуки startup / shutdown

Положите хуки жизненного цикла в hooks.py в корне приложения. Они выполняются на ASGI-событиях lifespan — это место для открытия/закрытия пулов, запуска планировщиков и т.д.

# hooks.py
from Services.db import pool

async def _connect():
    await pool.open()

async def _disconnect():
    await pool.close()

on_startup = [_connect]
on_shutdown = [_disconnect]

Хуки могут быть sync или async. Упавший startup-хук прерывает запуск (об этом сообщается ASGI-серверу); shutdown-хуки выполняются всегда.

Фоновые задачи (после ответа)

Верните работу, которая выполнится после отправки ответа — отлично подходит для писем, вебхуков, метрик:

from endocore import Response

async def send_receipt(order_id):
    await email.send(...)

async def handler(request):
    order = create_order(...)
    return Response.json({"id": order.id}, background=lambda: send_receipt(order.id))

Фоновый callable выполняется после того, как байты ответа отправлены, так что клиент не ждёт. Заблокирует ли он event loop, зависит от его точной формы — проверяется через inspect.iscoroutinefunction:

  • Передайте async def-функцию напрямую (без обёртки) — она будет await'ится прямо в event loop'е.
  • Что угодно другое — включая идиому lambda: send_receipt(order.id) выше, которая сама по себе обычный sync-callable, просто возвращающий корутину при вызове, — классифицируется как sync и диспетчеризуется в рабочий поток. В этой конкретной идиоме это безобидный холостой переход в поток (создание объекта корутины почти бесплатно; сама корутина потом await'ится обратно в loop'е как обычно), но это же значит, что по-настоящему блокирующий sync-callable, переданный как background= (def, который реально делает блокирующий I/O, а не просто lambda-обёртка), тоже корректно выполнится вне loop'а — точно так же, как тело sync-обработчика.

Тяжёлая или надёжная работа

Фоновые задачи — best-effort и внутри процесса. Для работы, которая должна переживать рестарты или масштабироваться, используйте очередь задач — см. расширение Celery.

Интеграции сервисов и lifespan

Расширения, перечисленные в extensions.py, подключаются при старте, а их startup/shutdown автоматически добавляются в lifespan — так RedisExtension открывает клиент на старте и закрывает при остановке без правок hooks.py. См. Расширения.