서비스
대기열 예매. 대기와 예매 처리를 분리
티케팅 서비스는 오픈 순간에 사용자가 몰리고 좌석은 4,000석입니다. 예매 처리가 1인당 트랜잭션과 DB 커넥션을 점유해 전체 인원을 그대로 받을 수 없는 구조라, 대기열이 입장 인원을 제한합니다. CGV 팀 프로젝트의 대기열을 queue(Go)와 booking(Spring) 두 서비스, Kafka로 다시 구성했습니다.
서비스 구조
관객 30, 정원 6, 좌석 24
정지 상태는 구조도, 재생 시 한 회 흐름을 표시합니다.
설계 결정
| 항목 | 선택 | 이유 |
|---|---|---|
| 서비스 분리 | 대기는 queue(Go), 예매는 booking(Java Spring) | 대기는 짧고 많은 요청이라 동시 처리 비용이 낮은 Go로, 예매는 결제와 정합성이라 트랜잭션이 검증된 Spring으로 |
| 대기열 상태 | Redis에 줄(waiting)과 정원(active)을 한 곳에 | 어느 queue 파드가 받아도 같은 순번과 정원을 응답 |
| 순번 조회 | 폴링. 순번이 뒤일수록 주기를 늘림(1초, 2초, 5초) | 대기 중 요청의 대부분이 순번 조회라, 먼 순번의 조회 횟수를 줄여 queue 부하를 낮춤 |
| 서비스 간 통신 | Kafka 비동기로 직접 호출 없음 | 동기 호출이면 booking이 느리거나 멈출 때 queue도 함께 멈춤. 처리하지 못한 입장 메시지는 토픽에 남음 |
| 좌석 확정 | MySQL 유니크 제약으로 같은 좌석 두 번 확정을 거절 | Redis 좌석 선점은 만료나 장애 때 같은 좌석이 중복될 수 있어, 최종 판정은 DB에서 |
| 계측 | 코드 계측으로 요청 하나를 trace_id로 서비스 간 추적 | 기본 metric으로는 어느 서비스의 어느 구간이 느린지 알 수 없음 |
결과
- queue(Go)와 booking(Spring)을 Kafka로 연결한 대기열 예매 서비스 구현: dev 공개 데모와 stg 부하 테스트를 같은 코드와 차트로 실행. 환경 차이는 값(정원, 커넥션 수, 자원 한도, 주소)뿐
- 대기와 예매 처리가 분리된 구조: 대기 인원은 queue가 받고 예매 여정은 정원 인원만 booking에서 진행. 예매 여정 요청이 정원에 묶이는 것은 부하 테스트에서 확인
한계
- Redis 주 노드 1대가 대기열 전담: 5만 명까지 한계에 닿지 않았고(엔진 CPU 37%) 분산 구성은 적용하지 않음
- 결제는 mock PG: 실제 결제 연동이 없고 좌석 선점부터 예매 완료까지만 구현
- 서비스 2개 규모의 MSA: 서비스가 많을 때의 서비스 간 통신과 배포 관리(서비스 메시 등)는 경험이 없음
기술 스택
Go, Spring Boot, Flyway, Redis, Kafka(Strimzi), MySQL, OpenTelemetry