"""insta-render FastAPI entry — health + lifespan (Browser pool + worker loop).""" from __future__ import annotations import asyncio import logging import os from contextlib import asynccontextmanager import redis.asyncio as aioredis from fastapi import FastAPI import card_renderer import worker from _shared.heartbeat import heartbeat_loop logging.basicConfig(level=logging.INFO, format="%(asctime)s %(name)s %(levelname)s %(message)s") logger = logging.getLogger(__name__) @asynccontextmanager async def lifespan(app: FastAPI): # Browser pool 초기화 (Chromium launch) await card_renderer.init_browser() # 큐 워커 백그라운드 시작 worker_task = asyncio.create_task(worker.worker_loop()) hb_redis = aioredis.from_url(os.getenv("REDIS_URL", "redis://192.168.45.54:6379"), decode_responses=False) hb_task = asyncio.create_task(heartbeat_loop(hb_redis, "insta-render", "render", worker.stats)) logger.info("insta-render lifespan 시작") try: yield finally: for t in (worker_task, hb_task): t.cancel() try: await t except asyncio.CancelledError: pass await hb_redis.aclose() await card_renderer.shutdown_browser() logger.info("insta-render lifespan 종료") app = FastAPI(lifespan=lifespan) @app.get("/health") def health(): return {"ok": True, "service": "insta-render"}