127 lines
4.1 KiB
Python
127 lines
4.1 KiB
Python
# ocr/api.py
|
|
import logging
|
|
|
|
import httpx
|
|
from config.setting import CELERY_FLOWER
|
|
from fastapi import FastAPI, HTTPException
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from prometheus_fastapi_instrumentator import Instrumentator
|
|
from router import ocr_api_router
|
|
from utils.celery_utils import celery_app
|
|
from utils.celery_utils import health_check as celery_health_check_task
|
|
from utils.redis_utils import get_redis_client
|
|
|
|
logging.basicConfig(
|
|
level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s - %(message)s"
|
|
)
|
|
|
|
|
|
app = FastAPI(title="OCR GATEWAY", description="OCR API 서비스", docs_url="/docs")
|
|
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=[
|
|
"http://172.16.42.101",
|
|
"http://gsim.hanmaceng.co.kr",
|
|
"http://gsim.hanmaceng.co.kr:6464",
|
|
],
|
|
allow_origin_regex=r"http://(172\.16\.\d{1,3}\.\d{1,3}|gsim\.hanmaceng\.co\.kr)(:\d+)?",
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
# Prometheus Metrics Exporter 활성화
|
|
Instrumentator().instrument(app).expose(app)
|
|
|
|
app.include_router(ocr_api_router)
|
|
|
|
|
|
@app.get("/health/API")
|
|
async def health_check():
|
|
"""애플리케이션 상태 확인"""
|
|
return {"status": "API ok"}
|
|
|
|
|
|
@app.get("/health/Redis")
|
|
def redis_health_check():
|
|
client = get_redis_client()
|
|
if client is None:
|
|
raise HTTPException(status_code=500, detail="Redis connection failed")
|
|
try:
|
|
client.ping()
|
|
return {"status": "Redis ok"}
|
|
except Exception:
|
|
raise HTTPException(status_code=500, detail="Redis ping failed")
|
|
|
|
|
|
@app.get("/health/Celery")
|
|
async def celery_health_check():
|
|
"""Celery 워커 상태 확인"""
|
|
# celery_app = get_celery_app() # 이제 celery_utils에서 직접 임포트합니다.
|
|
|
|
try:
|
|
# 1. 워커들에게 ping 보내기
|
|
active_workers = celery_app.control.ping(timeout=1.0)
|
|
if not active_workers:
|
|
raise HTTPException(
|
|
status_code=503, detail="No active Celery workers found."
|
|
)
|
|
|
|
# 2. 간단한 작업 실행하여 E2E 확인
|
|
task = celery_health_check_task.delay()
|
|
result = task.get(timeout=10) # 10초 타임아웃
|
|
|
|
if task.state == "SUCCESS" and result.get("status") == "ok":
|
|
return {
|
|
"status": "Celery is healthy",
|
|
"active_workers": active_workers,
|
|
"task_status": "SUCCESS",
|
|
}
|
|
else:
|
|
raise HTTPException(
|
|
status_code=500,
|
|
detail=f"Celery health check task failed with state: {task.state}",
|
|
)
|
|
|
|
except HTTPException as e:
|
|
# 이미 HTTPException인 경우 그대로 전달
|
|
raise e
|
|
except Exception as e:
|
|
logging.error(f"Celery health check failed: {e}", exc_info=True)
|
|
raise HTTPException(
|
|
status_code=500,
|
|
detail=f"An error occurred during Celery health check: {str(e)}",
|
|
)
|
|
|
|
|
|
@app.get("/health/Flower")
|
|
async def flower_health_check():
|
|
"""Flower 모니터링 대시보드 상태 확인"""
|
|
try:
|
|
flower_api_url = CELERY_FLOWER # Use the full URL from settings
|
|
async with httpx.AsyncClient(timeout=5.0) as client:
|
|
response = await client.get(flower_api_url)
|
|
response.raise_for_status()
|
|
|
|
# Just check if the API is reachable
|
|
if response.status_code == 200:
|
|
return {"status": "Flower is running"}
|
|
else:
|
|
raise HTTPException(
|
|
status_code=response.status_code,
|
|
detail=f"Flower API returned status {response.status_code}",
|
|
)
|
|
|
|
except httpx.RequestError as e:
|
|
logging.error(f"Could not connect to Flower: {e}", exc_info=True)
|
|
raise HTTPException(
|
|
status_code=503, detail=f"Could not connect to Flower: {str(e)}"
|
|
)
|
|
except Exception as e:
|
|
logging.error(f"Flower health check failed: {e}", exc_info=True)
|
|
raise HTTPException(
|
|
status_code=500,
|
|
detail=f"An error occurred during Flower health check: {str(e)}",
|
|
)
|