대기열 시스템 (Bufferable)

모든 코드는 github 에서 확인할 수 있습니다.

개요

특정 시점에 순간적으로 많은 사용자 요청이 발생하는 이벤트, 예를 들어 티켓팅, 수강신청, 또는 상품 프로모션의 경우, 요청을 순차적으로 처리하는 것이 매우 중요합니다. 이 글에서는 이러한 상황에서 요청을 대기시키고 순차적으로 처리하는 대기열 처리를 위한 라이브러리(Bufferable)를 설계/구현하고 사용예시를 소개합니다. 이를 통해 사용자 경험을 향상시키고, 시스템의 안정성을 확보할 수 있습니다.

대기열 시스템

설계

대기열정보를 관리하기 위해 레디스를 사용합니다. 레디스를 선택한 이유는 다음과 같습니다.

  • 싱글 스레드로 동작하여 동시성 이슈를 쉽게 처리가능
  • 인메모리 기반의 Key-Value 저장소로 보다 높은 성능으로 대기열을 관리
  • FIFO을 위한 다양한 데이터구조 지원

대기열을 관련 로직을 처리하기 위해 레디스의 List(FIFO 오퍼레이션을 사용해서 큐처럼 사용; lpush, rpop)와 Sorted Set을 고려할 수 있습니다. 이중 다음과 같은 이유로 Sorted Set을 사용했습니다.

  • 대기열에서 중요한 접근 시간이라는 스코어를 기준으로 FIFO 동작이 보장되어야함
    • Timestamp를 Score로 Sorted Set을 사용하면 요청 발생 순서로 처리가능
    • 게이트웨이의 인스턴스가 여러대인경우 List는 올바른 순서로 동작하지 않을 수 있음
  • List가 조금더 빠르나 Redis 자체가 높은 성능을 보장
    • 레디스의 List는 링크드리스트이므로 삽입, 제거시 O(1)
    • Sorted Set는 삽입, 제거시 O(log(n))

대기중인 사용자 관리와 함께 처리중인 요청 관리도 필요합니다. 대기열과 다르게 우선순위와 상관없이 서비스에 진입가능한 요청을 관리하므로 Set을 사용합니다. 레디스를 사용해서 처리큐와 대기큐를 관리하면 두개의 데이터 구조 관리(두 개 이상의 오퍼레이션)에 대한 원자성을 보장할 수 있습니다.

  • 처리큐: 현재 접근 가능한 요청 정보 관리
  • 대기큐: 대기중인 요청 정보 관리
  • 두개 이상의 오퍼레이션에 대한 원사정을 보장하기 위해 Lua Script 사용
    • 여러개의 오퍼레이션을 기반으로 원자성이 보장되는 동작을 구성할때는 해당 동작의 성능이 매우 중요
    • 싱글스레드 기반으로 동작하는 레디스의 경우 원자성 동작 안에서 데드락등 대기가 발생하면 전체 시스템 문제 발생

처리큐에 여유가 발생하는 경우 대기큐에 존재하는 요청을 처리큐로 옮겨와야합니다. 이를 위해 배치작업을 수행합니다. 위에서 레디스의 여러 오퍼레이션에 대한 원자성을 보장할 수 있다고 했습니다. 이는 대기큐에서 처리큐로 요청을 옮기는 작업에 원자성도 보장한다는 의미로 싱글 스레드 기반으로 동작하는 레디스 사용시 대기큐에서 처리큐로 요청을 옮기는 사이에 다른 동작이 수행될수 없다는 의미가 됩니다. 즉 동시성 이슈가 발생하지 않습니다.

동작방식

flowchart

요청이 발생했는데 토큰이 없거나 잘못된 토큰인 경우 토큰을 생성합니다. 이후 토큰이 처리큐 또는 대기큐에 존재하는지 확인하며 적절한 동작을 수행합니다.

구현

작성중입니다.

사용 예시

아키텍처

architecture

클라이언트

요청의 주체를 의미합니다. 일반적으로 웹 애플리케이션 또는 모바일 애플리케이션을 의미합니다.

게이트웨이

클라이언트 요청의 진입점으로서 인증, 인가 등의 공통요소를 처리합니다. 이후 처리해야할 백엔드 서비스로 라우팅합니다. 본 글에서는 대기열 처리를 위한 토큰 발급 및 토큰 유효성 검사 등을 수행하고 처리가능여부, 대기 여부등을 판단 후 사용자에게 적절한 응답을 반환합니다.

  • 사용자 요청에 대한 토큰 관리
    • 토큰을 기반으로 진입가능여부 판단
    • 토큰이 없으면 생성
    • Sanitize를 통한 잘못된(또는 악의적으로 변경된) 토큰 처리 방어

애플리케이션

게이트웨이에서 라우팅된 요청을 처리하는 애플리케이션입니다. 비즈니스 로직을 포함하고 있습니다.

테스트

작성중입니다.

정리

작성중입니다.