Fight World! Delivery

Personal Role




ERD




아키텍처




핵심 기능


- 예매 FLOW




- Redis를 활용한 예매 대기열

Redis를 사용한 이유



좌석 조회 시, running queue에 자리가 없으면 대기열(waiting queue)에 들어가게 된다.

사용자가 대기열에 진입한 후 새로고침 했을 때, 기존 대기번호를 유지하지 않고 대기열의 끝으로 이동하여 새로운 대기번호를 부여한다.




waiting:queue (대기열)

사용자들을 대기 순서대로 저장하기 위해 Redis의 Sorted Set을 사용

사용자를 대기열에 등록할 때, 현재 시간을 점수로 하여 Reids의 ZSET에 추가

대기열에서 가장 먼저 등록된 사용자를 pop으로 가져오고 대기열에서 제거 한 후 running:queue로 사용자를 이동


running:queue(활성 큐)

Redis의 SET 자료구조를 사용하여 현재 주문을 진행 할 수 있는 사용자들을 관리

SET은 중복을 허용하지 않으므로 동일한 사용자가 등록 되는 것을 방지


- 대기열 부하테스트(JMeter)

✅ 테스트 목적: 1000명의 사용자가 동시에 서버에 HTTP 요청을 보냈을 때의 응답 성능을 평가하고, 서버의 처리 능력과 안정성을 확인





성능: 평균 응답 시간이 43ms, 최대 응답 시간은 131ms로, 모든 요청이 200ms 이내에 응답됨

안정성: 0%의 오류율로 모든 요청이 성공적으로 처리되었음을 확인

처리량: 처리량은 803.2/sec로, 서버가 1000명의 동시 요청을 충분히 처리할 수 있음을 보여줌

데이터 전송: 수신 속도와 전송 속도는 각각 519.86KB/초와 141.19KB/초로, 요청과 응답 데이터 처리량이 적절한 수준으로 이루어졌음


Troubleshooting


대기열 - 대규모 동시 접속(10000명) 테스트에서 Running Queue의 최대 용량이 10명으로 제한되어 있음에도 불구하고,

실제로는 26명이 들어가는 문제 발생



Expected: Running Queue 10명

Actual: Running Queue 26명



Redis 작업의 원자성 부재

size() 체크와 putIfAbsent() 작업이 분리되어 실행됨

두 작업 사이에 다른 스레드가 개입할 수 있는 가능성 존재


동시성 제어 미흡

여러 스레드가 동시에 available() 체크를 통과할 수 있음

size가 9일 때 여러 스레드가 동시에 true를 반환받아 push를 시도하게 됨

결과적으로 MAX_CAPACITY를 초과하는 데이터가 저장됨


🔑 해결 방안



Redis Transaction 활용

WATCH-MULTI-EXEC 패턴 적용

큐의 상태가 변경되면 트랜잭션이 자동으로 실패


원자성 보장

크기 체크와 데이터 추가를 하나의 트랜잭션으로 처리

중간 상태 없이 모든 작업이 한 번에 성공하거나 실패


Optimistic Lock 구현

WATCH 명령어로 낙관적 락 구현

다른 클라이언트가 키를 수정하면 트랜잭션 실패




Running Queue 크기가 정확히 10명으로 제한됨

동시성 문제 해결

대기열 시스템의 안정성 확보

대규모 트래픽 티켓 예매 시스템

MSA 구조의 예매 시스템이며, 특정 시간에 사용자의 요청이 몰리는 온라인 예매 시스템에서 대규모 트래픽을 안정적으로 처리하는 것을 목표로 하는 Java, Spring Boot 기반 백엔드 팀 프로젝트 입니다.

  • 개발인원: 4명
  • Github: https://github.com/TEAM21-2NE1/ticketing
  • Language: Java 17
  • Framework: SpringBoot3
  • Database: Postgresql, Redis, Amazon S3
  • Messaging: Kafka
  • Monitoring: Prometheus, Grafana
  • Test: Jmeter
  • API: PortOne
  • CI/CD: Git Actions, ECR, EC2, Docker