-
이코에코(Eco²) Message Queue #13: Scan API 성능 측정 (SSE + Celery Chain + Gevent)이코에코(Eco²)/Message Queue 2025. 12. 25. 04:57
Celery Chain + Gevent Pool 기반 비동기 아키텍처 전환 후 실제 성능을 측정하고, HTTP 1.1 + gRPC + asyncio와 비교합니다.항목 내용 테스트 일시 2025-12-25 02:16 ~ 04:35 (KST) 테스트 도구 k6 (JavaScript 기반 부하 테스트) 대상 엔드포인트 /api/v1/scan/classify/completion(SSE)모니터링 Prometheus + Grafana (Scan SSE Pipeline 대시보드)
1. 테스트 환경
1.1 Queueing 아키텍처

1.2 Worker 설정
scan-worker gevent 100 1~5 character-match-worker gevent 50 1~4 character-worker gevent 50 1~2 my-worker gevent 50 1~2 1.3 k6 테스트 스크립트
export const options = { stages: [ { duration: "60s", target: 34 }, // 0 → 34 VU ramp-up { duration: "90s", target: 34 }, // 34 VU 유지 { duration: "30s", target: 0 }, // ramp-down ], thresholds: { http_req_failed: ["rate<0.3"], // 실패율 30% 미만 sse_total_duration: ["p(95)<30000"], // 95%가 30초 이내 sse_ttfb: ["p(95)<8000"], // TTFB 95%가 8초 이내 }, };
2. 테스트 결과
2.1 테스트 케이스 요약
1 02:16:01 ~ 02:17:45 10 100% 100% 링크 2 02:59:01 ~ 03:00:12 34 82.8% 100% 링크 3 04:31:59 ~ 04:34:45 30 95.1% 100% 링크 k6 vs Grafana 불일치: k6에서 503 에러는 Istio/Envoy 레벨에서
no healthy upstream발생.
Grafana는 Celery 레벨 성공률 측정 → Worker가 처리한 Task는 100% 성공.
2.2 테스트 #1: 저부하 (k6, VU 10, non-HA)

Grafana 메트릭

https://snapshots.raintank.io/dashboard/snapshot/QB6LdIZwjFLXiNVQq1xljAQ3vnVT77a3
Chain Avg Duration 11.3s TTFB (p50) 1.74s Success Rate 100% RPS 0.10 req/s Active Connections (max) ~10 스테이지별 평균 소요 시간:
- vision: ~4.5s (OpenAI Vision API)
- rule: ~0.3s (DB 조회)
- answer: ~4.8s (OpenAI Chat API)
- reward: ~1.7s (Character Match + DB)
2.3 테스트 #2: 고부하 (k6, VU 34)

Grafana 메트릭
https://snapshots.raintank.io/dashboard/snapshot/ULL5FNeft8dN9yU25MYyKyEoOrMxbagk
Chain Avg Duration 19.0s TTFB (p50) 1.07s Success Rate 90.7% RPS 1.10 req/s Active Connections (peak) 25 문제 분석:
503 no healthy upstream: scan-api Pod가 과부하로 liveness probe 실패 → 재시작- HPA가 스케일아웃 되기 전에 Pod 불안정 발생
- Celery Worker는 정상 처리 (Grafana Success 100%)
2.4 테스트 #3: 안정화 (k6, VU 30)

Grafana 메트릭

https://snapshots.raintank.io/dashboard/snapshot/FqFEJTRNVC3G9SQbH0DWc2EluC7RlFzU
Chain Avg Duration 21.1s TTFB (p50) 1.32s Success Rate 100% RPS 0.27 req/s 관찰 사항:
- VU 30으로 감소 → 95.1% 성공률 달성
- p99 응답 시간이 ~25s로 증가 (OpenAI rate limit 근접)
- Reward Null 73.2%: 캐릭터 매칭 로직 이슈 (별도 수정 필요)
3. 버전별 Scan API 성능표
3.1 Scan API v1.0.0 (asyncio.to_thread, only HTTP 1.1)
테스트 결과 (2025-12-08):
5명 10s 15s 100% 10명 11s 16s 100% 100명 - 150s+ 0% 병목 분석:
- GIL로 인한 CPU 처리 병목
- 스레드풀 크기 제한 (2+4 = 6 concurrent)
- GPT I/O가 70~80% 점유
3.2 /scan/classify (HTTP 1.1+gRPC, v1.0.7) vs /scan/classify/completion (SSE, v1.0.7)


동시 처리 60명 (스레드 2+4, HPA 1-4), Success 98+% 30 greenlets (HPA 1-4), 100% 평균 응답 시간 10~22초 11~21초 성공률 98.3% 95.1% (50명 기준 성공률 38.7%) 메모리 사용 4GB ~1GB (gevent) 스케일링 HPA 자동 (1-4) HPA 자동 (1-4) 병목 GIL + 스레드풀 SSE 연결 부하, Celery Task Grafana Snapshot p99 10-19.4s, avg 6s, CPU 0.04, Memory 174-300MB p99 49s, avg 13s, CPU 0.25, Memory 512MB+
4. 핵심 지표 분석
4.1 스테이지별 소요 시간
vision 4.5s 6~10s 40% OpenAI Vision API rule 0.3s 2~3s 5% PostgreSQL 규칙 조회 answer 4.8s 8~15s 45% OpenAI Chat API reward 1.7s 3~5s 10% 캐릭터 매칭 + DB 소요 시간 비율:
vision (40%) rule(5%) answer (45%) reward(10%) ├────────────────────────────┼────┼─────────────────────────────┼───────┤ │█████████████████████████████│████│█████████████████████████████│███████│ └────────────────────────────────────────────────────────────────────────┘ 0s 5s 6s 11s 13s핵심 병목: OpenAI API 호출 (vision + answer)이 전체 소요 시간의 85% 차지.
4.2 TTFB 분석
저부하 (VU 10) 1.74s 5.7s 정상 고부하 (VU 34) 1.07s 3.4s 빠름 (OpenAI 캐시 히트 추정) 안정화 (VU 30) 1.32s 16.3s OpenAI 지연 TTFB = 첫 번째 SSE 이벤트 도착 시간 (vision task 시작 시점)
5. 개선 방향
OpenAI 동기 호출 OpenAI Batch API (50% 비용 절감) Rate Limit 500 RPM Tier 업그레이드 or Multi-Provider scan-api HPA CPU 70% startupProbe 조정, 초기 replica 증가 캐시 규칙 DB 조회 Redis 캐싱 (rule 단계 최적화) Celery Chain 동기식 Task Chain, 큐 부하 LangChain/LangGraph로 경량화, run_id 단위 큐잉
6. 결론
6.1 성과
- prefork 대비 안정성: 30명 초과 부하에서 0% → 95% 성공률
- 메모리 효율: 4GB → 1GB (75% 절감)
- 자동 스케일링: HPA 기반 탄력적 확장
6.2 한계
- OpenAI Rate Limit: LLM IO 바운드가 상한, 보다 높은 Tier 혹은 멀티 프로바이더 재고
- TTFB 증가: 부하 증가 시 p95 16s까지 상승
- 큐잉 경량화 필요: LLM 체이닝을 Celery Chain으로 풀어 Task(동기) 및 큐잉 부하가 큼, LangChain/LangGraph로 경량화 필요
- SSE 연결 부하 비용 높음: 기존 HTTP 1.1에 비해 스트리밍 방식인 SSE의 부하가 큶. 실시간 단계별 Event 전송으로 UX 상승 여력 존재.
6.3 다음 단계
- OpenAI Batch API 적용 검토
- LangChain/LangGraph 마이그레이션 검토, run_id 단위로 큐잉 고려
- Rate Limit 대응 (Multi-Key 또는 Tier 업그레이드)
관련 문서
대시보드 정의
- /backend/workloads/monitoring/dashboards/scan-sse-pipeline.yaml
- /backend/workloads/monitoring/dashboards/domain-scan-api.yaml
'이코에코(Eco²) > Message Queue' 카테고리의 다른 글