Фоновые задачи и жизненный цикл¶
На этой странице две вещи: код, который выполняется при старте и остановке приложения (открыть пул БД, закрыть его), и работа, отложенная до момента после отправки ответа (отправить письмо, не заставляя клиента ждать).
Хуки 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.
См. Расширения.