티스토리 뷰

Infra

지마켓 대기열 시스템 파헤치기

지마켓 김윤제 2022. 9. 30. 13:07

지마켓 대기열 시스템 파헤치기

안녕하세요 VIP & Vertical 팀 김윤제입니다.

VIP 파트에서 상품 상세 페이지 및 리뷰 업무를 맡고 있습니다.

이번 블로깅에서는 Auction, Gmarket에서 사용하고 있는 대기열 시스템인 Redcarpet에 대해 소개하려 합니다.

 


Redcarpet

Redcarpet 로고

Redcarpet은 Auction, Gmarket의 대기열 시스템으로 일시적으로 많은 트래픽이 발생하는 서비스에
과도한 트래픽의 유입을 방지해 주고 시스템을 보호합니다.

 

대기열 시스템 Redcarpet의 이름은 대형 홀 등의 줄을 서서 기다리는 곳에 깔린 레드카펫에서 유래되었습니다.


Redcarpet의 도입이유

 

Big Smile Day, Big Sale 등의 이벤트 또는 인기 있는 상품에 대해 트래픽이 몰리는 순간이 있습니다.

 

이렇게 순간적으로 높은 트래픽을 처리하기 위해 서비스를 Scale out 하는 것은 아주 큰 비용이 발생하고,

더 이상 Scale out 할 수 없는 시스템이 포함되어 있는 경우 한계가 있습니다.

 

예측된 트래픽을 대비해 Scale out을 하더라도 트래픽이 폭주하는 짧은 순간이 지나가면 Scale out 된 대부분의 자원은 낭비됩니다.

 

이러한 문제를 해결하기 위해 Auction, Gmarket은 In-Memory 저장소인 Redis를 활용하여 대기열 아키텍처(Redcarpet)를 도입했습니다.


왜 Redis인가?

  • Redis의 데이터 구조 중 Sorted Set은 Key, Score, Member의 형태로 이루어져 있으며 Score를 기반으로 정렬이 됩니다.
    • 이러한 이유 때문에 RDB를 사용하여 정렬하기보단, Redis를 이용하는 게 성능적인 면에서 우세합니다.
  • Kafka 등 다른 메시지 큐를 활용한 방안도 있었을 텐데?
    • Redcarpet은 실시간 랭킹 서비스를 제공하기 때문에 비동기 메시지 큐를 활용하기엔 적절하지 않았습니다.
  • 개별 사용자의 Request에서 Timestamp를 Score로, 사용자 식별 키 값을 Member으로 사용하는 아이템을 Sorted Set에 저장하고, 설정된 유입량에 따라 낮은 Score의 아이템들을 제거해 나감으로써 마치 FIFO Queue와 같이 동작하지만 Timestamp로 기록된 Score를 활용하여 다양하고 섬세한 기능이 구현되었습니다.
  • 높은 퍼포먼스를 가지고 있는 Redis를 충분히 활용하기 위해 API 서비스는 Non-Blocking 처리가 편리한 Node.js으로 작성되었으며, Docker 이미지로 빌드되어 Kubernetes에 의해 Docker 컨테이너로 관리됩니다.
    • 컨테이너를 유동적으로 늘릴 수 있는 환경에서 이벤트 상황에 따라 충분히 Scale-out 하여 높은 부하를 감당할 수 있도록 구성했습니다.

Redcarpet 구조도

Redcarpet은 아래와 같이 Auction, Gmarket의 Private Cloud Flatform 환경 안에서 3개의 시스템과
외부의 Redis, 모니터링 시스템, 어드민 툴을 이루어져 있습니다.

  • Front-end webpage
    • 사용자의 웹브라우저에 대기 중 상태의 웹페이지를 서비스합니다.
  • Web API Service
    • 대기열의 주요 기능들 [대기열 등록, 조회 등]을 제공하는 API 서비스입니다.
  • Job Service
    • 주기적으로 Redis Queue에 쌓인 사용자들을 이동시키고, 모니터링을 위한 정보를 기록합니다.
  • Admin Tool
    • 대기열의 등록, 수정 등을 통해 실시간으로 유입량을 조절할 수 있는 관리 도구입니다.

Redcarpet Redirect 동작 과정

1번 과정 설명

가). 상품 상세에서 Web Service API에 대기 필요 여부에 대해 조회 요청을 합니다.

 

나). Web Service API에서는 자동 대기열 세팅이 필요한지를 파악하기 위해 해당 상품에 대한 트래픽 정보를 Redis에 저장합니다.

 

다). 자동 대기열 세팅이 되어있는지 파악하기 위해 Redis의 모든 자동 대기열 세팅 Key로 이루어진 데이터를 가져와 해당 상품의 번호, 이벤트 타입 등의 조합으로 이루어진 Field를 찾습니다.

 

라). [다]에서 자동 대기열 세팅이 되어 있지 않다면 [나]에서 저장한 트래픽 정보를 가져와 트래픽이 특정 임계치 이상인지 비교합니다.

 

마). 특정 임계치 이하라면 VIP(상품 상세)를 보여주도록 반환합니다.

 

사). 특정 임계치 이상이면 조합된 자동 대기열 세팅 정보를 Redis에 저장합니다.

 

아). Redis에 Wating이라는 이름을 가진 Key, 현재 시간을 Score, 유저 아이디와 상품 번호를 Member로 저장합니다.

 

1) Redis의 Zrank를 이용하여 해당 Key와 Member로 대기 순번을 조회 및 대기 임계치와 비교 후 대기 여부를 반환합니다.

 

 

2번 과정 설명

가). Front Web Page에서 Web Service API로 대기 유지 필요 유무에 대해 요청합니다.

 

나). Web Service API에서는 자동 대기열 세팅이 되어있는지 파악하기 위해 Redis의 모든 자동 대기열 세팅 Key로 이루어진 데이터를 가져와 해당 상품의 번호, 이벤트 타입 등의 조합으로 이루어진 Field를 찾습니다.

  1. Field가 없다면 VIP(상품 상세)를 보여주도록 반환합니다.

  2. 위 1번 과정에서 Wating이라는 이름을 가진 Key와 유저 아이디와 상품번호로 구성된 Member 조합으로 Redis 저장된 데이터를 조회하여 대기 여부를 반환합니다.

Job Service 동작 과정

Redcarpet Job Service는 Wating, Running, Finish 3가지의 키를 이용하여 주기적으로 Redis Queue에 쌓인 사용자들을 이동시키고, 모니터링을 위한 정보를 기록합니다.

  • Waiting Key : 최초 대기열 진입한 Client 데이터 관리용 Key

  • Running Key : 대기열을 통과한 Client 데이터 관리용 Key

  • Finished Key : 대기열 통과 후 Session이 종료된 Client 데이터 관리용 Key

 

 

1. 초당 유입량, 활성화된 Job 개수, 현재 Job의 순번을 통해 각 Job에 할당된 유입량을 설정합니다.

  • Redis에 Waiting Key로 저장된 값들을 Zrange를 통해 0번부터 위에서 설정한 할당된 유입량까지 조회합니다.

 

2. Redis에 Running Key로 위에서 구한 데이터들을 Member로 현재 시간을 Score로 저장합니다.

 

3. Running Key에 데이터 저장이 끝나면 위에서 조회한 Waiting Key에 저장된 데이터들을 ZrememberByRank를 이용해 삭제합니다.

 

4. 위 과정과 같이 Running Key로 저장된 데이터들은 또 다른 Job이 실행되어 Finish Key로 옮겨 가게 되며 Waiting, Running, Finish에 저장된 데이터들의 추이는 아래 그래프의 형태와 같게 됩니다.


Redcarpet 모니터링 시스템

InfluxDB에 쌓인 데이터를 Grafana 툴을 이용해 조회할 수 있도록 모니터링 시스템이 구축되어 있습니다.

이를 통해 실시간 트래픽, 대기열 현황, 지난 1분간 상품 조회 순위 등을 확인할 수 있습니다.


Redcarpet 도입 효과

Redcarpet 도입 이후로 Auction, Gmarket의 가장 큰 행사인 Big Smile Day 뿐만 아니라, 가장 많은 트래픽이 몰렸던

HOT 콘서트도 문제없이 소화했습니다.


대기열 페이지 내 소소한 게임

Gmarket은 대기열 사용자 경험 개선을 위해 대기열 페이지 내에 다양한 게임을 제공하고 있습니다.

게임의 설정은 Admin Tool에서 이루어지고 있으며 랜덤 하게 로딩되고 있습니다.


마치며

지금까지 Auction, Gmarket의 대기열 시스템인 Redcarpet에 대해서 알아보았습니다.

 

도움 주신 분들께 감사의 말씀드립니다.

 

긴 글 읽어주셔서 감사합니다.

Reference

https://redis.io/docs/

 

댓글