Step 9: 고급 이벤트 처리 시스템 구현
주요 기능: - Kafka 이벤트 컨슈머 및 프로듀서 통합 - 지수 백오프 재시도 메커니즘 구현 - Dead Letter Queue (DLQ) 설정 - 이벤트 스키마 레지스트리 (Pydantic v2 호환) - Console 서비스에 이벤트 관리 API 추가 - 실시간 이벤트 통계 및 모니터링 - 엔드-투-엔드 테스트 스크립트 구현된 이벤트 타입: - USER_CREATED, USER_UPDATED, USER_DELETED - OAUTH_APP_CREATED, OAUTH_TOKEN_ISSUED - IMAGE_UPLOADED, IMAGE_PROCESSED 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -5,7 +5,10 @@ import uvicorn
|
||||
from datetime import datetime, timedelta
|
||||
import httpx
|
||||
import os
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import Any
|
||||
from contextlib import asynccontextmanager
|
||||
from auth import (
|
||||
Token, UserLogin, UserInDB,
|
||||
verify_password, get_password_hash,
|
||||
@ -13,10 +16,51 @@ from auth import (
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES
|
||||
)
|
||||
|
||||
# Import event consumer
|
||||
from event_consumer import AdvancedEventConsumer
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Global event consumer instance
|
||||
event_consumer = None
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
# Startup
|
||||
global event_consumer
|
||||
|
||||
try:
|
||||
# Initialize and start event consumer
|
||||
event_consumer = AdvancedEventConsumer(
|
||||
topics=["user-events", "oauth-events"],
|
||||
group_id="console-consumer-group",
|
||||
redis_url=os.getenv("REDIS_URL", "redis://redis:6379"),
|
||||
bootstrap_servers=os.getenv("KAFKA_BOOTSTRAP_SERVERS", "kafka:9092"),
|
||||
enable_dlq=True,
|
||||
dlq_topic="dead-letter-queue"
|
||||
)
|
||||
|
||||
await event_consumer.start()
|
||||
logger.info("Event consumer started successfully")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to start event consumer: {e}")
|
||||
# Continue without event consumer (degraded mode)
|
||||
event_consumer = None
|
||||
|
||||
yield
|
||||
|
||||
# Shutdown
|
||||
if event_consumer:
|
||||
await event_consumer.stop()
|
||||
logger.info("Event consumer stopped")
|
||||
|
||||
app = FastAPI(
|
||||
title="Console API Gateway",
|
||||
description="Central orchestrator for microservices",
|
||||
version="0.1.0"
|
||||
version="0.1.0",
|
||||
lifespan=lifespan
|
||||
)
|
||||
|
||||
# Service URLs from environment
|
||||
@ -45,6 +89,66 @@ async def health_check():
|
||||
return {
|
||||
"status": "healthy",
|
||||
"service": "console",
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"event_consumer": "running" if event_consumer else "not running"
|
||||
}
|
||||
|
||||
# Event Management Endpoints
|
||||
@app.get("/api/events/stats")
|
||||
async def get_event_stats(current_user = Depends(get_current_user)):
|
||||
"""Get event consumer statistics"""
|
||||
if not event_consumer:
|
||||
raise HTTPException(status_code=503, detail="Event consumer not available")
|
||||
|
||||
return {
|
||||
"stats": event_consumer.stats,
|
||||
"timestamp": datetime.now().isoformat()
|
||||
}
|
||||
|
||||
@app.get("/api/events/dlq")
|
||||
async def get_dlq_messages(
|
||||
limit: int = 10,
|
||||
current_user = Depends(get_current_user)
|
||||
):
|
||||
"""Get messages from Dead Letter Queue"""
|
||||
if not event_consumer:
|
||||
raise HTTPException(status_code=503, detail="Event consumer not available")
|
||||
|
||||
messages = await event_consumer.get_dlq_messages(limit=limit)
|
||||
return {
|
||||
"messages": messages,
|
||||
"count": len(messages),
|
||||
"timestamp": datetime.now().isoformat()
|
||||
}
|
||||
|
||||
@app.post("/api/events/dlq/{event_id}/retry")
|
||||
async def retry_dlq_message(
|
||||
event_id: str,
|
||||
current_user = Depends(get_current_user)
|
||||
):
|
||||
"""Manually retry a message from DLQ"""
|
||||
if not event_consumer:
|
||||
raise HTTPException(status_code=503, detail="Event consumer not available")
|
||||
|
||||
success = await event_consumer.retry_dlq_message(event_id)
|
||||
if not success:
|
||||
raise HTTPException(status_code=404, detail="Event not found in DLQ")
|
||||
|
||||
return {
|
||||
"status": "retry_initiated",
|
||||
"event_id": event_id,
|
||||
"timestamp": datetime.now().isoformat()
|
||||
}
|
||||
|
||||
@app.get("/api/events/schemas")
|
||||
async def get_event_schemas():
|
||||
"""Get all event schemas documentation"""
|
||||
from shared.kafka.schema_registry import SchemaRegistry
|
||||
|
||||
schemas = SchemaRegistry.get_all_schemas()
|
||||
return {
|
||||
"schemas": schemas,
|
||||
"version": "1.0.0",
|
||||
"timestamp": datetime.now().isoformat()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user