Skip to content
Jun Kim edited this page Jul 19, 2024 · 6 revisions

녹화 영상

화면-녹화-영상-2


아키텍처 디자인:

콘서트 티켓팅 서비스를 제공하는 웹 백엔드 기획
콘서트 예매 오픈 시간에 티켓 예매 요청에 대량의 트래픽이 몰리는 상황을 가정하여 이를 견고하게 견딜 수 있는 아키텍처 구상 대용량의 트래픽을 대기열을 통해 비동기 방식으로 buffer를 주어 처리하되, 실시간으로 예매 순번을 조회할 수 있도록 구현 kafka를 기준으로 producing 하는 main-server와 consuming 하는 consumer-server 로 모듈을 분리


ERD:


전체 예매 백엔드 플로우

[메인 서버]:

  1. 회원가입 / 로그인: 사용자는 메인 서버에서 회원가입 및 로그인을 통해 인증 / 인가 진행.
  2. 콘서트 목록 및 상세 페이지: 사용자는 콘서트 목록에서 원하는 콘서트의 상세 페이지로 진입.
  3. 예매 요청 처리: 사용자가 예매 요청을 하면, 해당 사용자의 고유값과 콘서트의 고유값 등을 포함한 메시지를 Kafka에 produce. [컨슈머 서버]:
  4. 메시지 소비 및 처리:
    • @KafkaListener를 사용하여 Kafka에서 메시지를 consume.
    • 메시지를 파싱하여 사용자와 콘서트 정보를 기반으로 순번을 할당하고 예매 데이터를 생성. (이 시점부터 예매 대기 순번 확인 가능)
  5. 예매 상태 업데이트:
    • @Scheduled를 사용하여 일정 시간이 지난 예매 건을 EXPIRED 상태로 업데이트.
    • 공연의 예매 가능 수량을 기준으로 AVAILABLE 상태로 업데이트. [메인 서버]:
  6. 예매 상태에 따른 화면 전환:
    • WAIT 상태에서는 예매 대기 화면을 표시.
    • AVAILABLE 상태로 변경되면, 예매 페이지로 진입하여 결제 과정을 진행

서버 확장성을 고려한 설계

인기 있는 콘서트의 경우 콘서트 예매 가능 시간에 대규모 트래픽이 발생합니다. 많은 고객에게 편안한 사용자 경험을 제공하기 위해서 지속적으로 서버 확장이 가능하고 트래픽을 분산시킬 수 있는 scale out이 용이한 설계를 고민했습니다. 고민 결과, 모든 트래픽을 바로 예매 가능한 상태로 만들어 한정된 좌석에 접근하는 방식은 비효율적이라고 판단했고, 예매 플로우 중간에 buffer를 주어 예매 요청을 통한 순번 지정은 real-time으로 하되, 그 외 기능은 서버 뒷 단에서 처리되도록 설계했습니다. NGrinder를 통해 2 agents * 3 processes * 5 threads 의 환경으로 로그인, 공연 예매 요청을 수행하도록 20분 동안 부하를 주어 테스트 해 본 결과, 로컬 서버 기준 402.1 TPS 를 73.9 ms 응답 속도로 에러 없이 안정적으로 유지했습니다. 또한 전체 약 48만 건의 요청은, 이를 consume 하는 서버의 확장을 통해 real-time으로 예매 데이터 저장 및 순번 지정이 가능함을 확인했습니다.

분산락을 통한 동시성 문제 방지

예매 요청시 대부분의 사용자는 대기열 페이지에서 대기 상태(WAIT)로 기다리며, 서버 뒷단의 처리에 의해 순서에 따라 예매 가능 상태(AVAILABLE)로 변경됩니다. 이후 좌석 선택 / 결제 진행 페이지로 진입하게 되는데, 사용자 간 동일한 좌석을 예매하거나 결제 도중 예매 상태 데이터의 변경(EXPIRED)이 발생하는 등의 동시성 문제가 예상되었습니다. 대용량 트래픽을 견디기 위한 설계였기에 해당 기능을 처리하는 메인 서버의 스케일 아웃 시 각각의 인스턴스에서 동일 자원에 대한 동시 접근을 방지하는 분산락이 필요하다고 생각했습니다. 기존에는 멀티 스레드 환경에서의 동시성 문제를 방지하기 위해 ConcurrentHashMap 자료구조를 통해 락을 걸어두었습니다. 하지만 확장된 다중 서버 환경에서의 동시성 문제도 방지할 수 있는 분산락을 적용하기 위해, 메모리 기반의 원자적 연산이 가능한 글로벌 캐시인 Redis를 도입하기로 결정했습니다. multi를 통한 트랜잭션 내 watch와 setIfAbsent 를 통해 견고한 원자적 연산이 이루어지도록 구현하여 다중 서버에서의 동시성 문제를 방지할 수 있었습니다.