Перейти к основному содержимому

lifespan

lifespan — это ASGI-протокол, который вызывается при запуске и остановке вашего приложения.

lihil ожидает lifespan со следующим интерфейсом:

type LifeSpan = Callable[
["Lihil"], AsyncContextManager[None] | AsyncGenerator[None, None]
]

Пример:


async def example_lifespan(app: Lihil):
engine = app.graph.resolve(create_async_engine)
await engine.execute(text("SELECT 1"))
yield
await engine.dispose()

lhl = Lihil(lifespan=example_lifespan)

когда использовать lifespan и что делать в обработчике lifespan

Обработчики lifespan ASGI — отличный способ управления логикой запуска и завершения для асинхронных приложений. Эти обработчики выполняются один раз при запуске приложения и при его остановке, что делает их идеальными для управления ресурсами, которые нужно инициализировать и очистить асинхронно.

Тестирование доступности сервиса

from sqlalchemy.ext.asyncio import create_async_engine

async def lifespan(app: Lihil):
engine = create_async_engine(app.config.database.url)
await engine.execute(text("SELECT 1"))
yield
await engine.dispose() # закрываем соединения при завершении

await engine.execute(text("SELECT 1")) отправляет пустой запрос к вашей базе данных и таким образом тестирует доступность сервиса, и если он не удастся, приложение быстро завершится без запуска.

Создание синглтон-объектов, которые требуют цикл событий

При работе с асинхронными клиентами или сервисами, такими как продюсеры Kafka или клиенты баз данных, вам нужно убедиться, что они запущены в рамках цикла событий:

from aiokafka import AIOKafkaProducer

async def lifespan(app: Lihil):
kafka = AIOKafkaProducer(bootstrap_servers=app.config.kafka.url)
await kafka.start()
yield
await kafka.stop()

Здесь продюсер Kafka создается как синглтон-объект в рамках обработчика lifespan. Продюсер подключается к брокеру Kafka при запуске и корректно завершается при остановке приложения.

Очистка ресурсов

Во время завершения важно очистить все соединения, освободить ресурсы или корректно остановить сервисы:

async def lifespan(app: Lihil):
engine = create_async_engine(...)
kafka = AIOKafkaProducer(...)
await kafka.start()
yield
await kafka.stop() # Остановка продюсера Kafka корректно
await engine.dispose() # Освобождение движка или любых соединений с базой данных

Логирование

Обработчики lifespan также хорошее место для логирования ключевых событий, таких как запуск и завершение приложения:

import logging

logger = logging.getLogger(__name__)

async def lifespan(app):
logger.info("App is starting...")
await engine.execute(text("SELECT 1"))
yield
logger.info("App is shutting down...")
await engine.dispose()

Регистрация метрик (например, Prometheus)

В некоторых случаях вы можете захотеть инициализировать сервисы мониторинга или метрик при запуске:

from prometheus_client import start_http_server, Counter

metrics_counter = Counter('app_start', 'App has started')

async def lifespan(app):
# Запуск сервера метрик Prometheus на отдельном порту
start_http_server(app.config.prometheus.url)
metrics_counter.inc() # Увеличение счетчика для отслеживания запуска приложения
yield

Планирование фоновых задач или инициализация планировщика задач

Если ваше приложение нуждается в планировании периодических задач (например, используя Celery или другой планировщик), вы можете сделать это в обработчике lifespan:

import asyncio

async def schedule_tasks():
while True:
print("Running periodic task...")
await asyncio.sleep(60) # Выполняется каждые 60 секунд

async def lifespan(app):
task = asyncio.create_task(schedule_tasks())
yield
# Отмена фоновой задачи при завершении
task.cancel()
try:
await task
except asyncio.CancelledError:
pass

Предварительная загрузка кэшей или данных в памяти

Если ваше приложение полагается на предварительную загрузку определенных данных (таких как кэширование или конфигурация), вы можете сделать это в обработчике lifespan для ускорения запуска приложения:

async def preload_cache():
# Пример: Предварительная загрузка часто используемых данных в кэш
await cache.set("key", "value")

async def lifespan(app):
# Предварительная загрузка кэша при запуске приложения
await preload_cache()
yield

Технические детали

Когда ASGI-сервер (например, uvicorn) запускается и останавливается, он отправляет событие lifespan веб-фреймворку, который он хостит (например, lihil). Когда lihil получает сообщение lifespan, он сначала запускает предоставленный пользователем обработчик lifespan (если он есть), затем выполняет внутренние настройки.