처리율 제한 장치 도입 ( feat. SpringBoot, TokenBucket)

Woo Yong·2024년 5월 22일
1

프로젝트 회고

목록 보기
4/8
post-thumbnail

프로젝트를 진행하는데 있어서 회원가입 시 이메일 인증 기능을 구현하게 되었다.

이메일 인증을 구현하기 위해서 구글 SMTP를 이용하여 구현하였다. 이처럼 구글 SMTP는 구글 서버에서 제공되는 서비스이기 때문에 외부 API를 호출하는 것과 같다.

또한 구글 SMTP 서비스는 하루에 500개의 전송 한도가 정해져 있고, 악의적인 사용자가 반복적으로 인증 이메일을 요청하게 될 경우에는 이메일 인증 기능이 정상적으로 작동하지 않아 회원가입도 불가능하게 되는 상황까지 유발할 것이라고 생각이들었다. 이러한 문제점을 해결하기 처리율 제한 장치를 도입한 과정에 대해 회고록을 작성해보려고 한다.

처리율 제한 알고리즘

처리율 제한 기능을 구현하기 위해서 공부하던 중, 다양한 처리율 제한 알고리즘이 있다는 것을 알게되었다.

그리고 처리율 제한 기능을 구현하기 위해서 어떠한 알고리즘을 선택하는게 좋을지 생각해보려고 한다.

토큰 버킷 알고리즘 (token bucket)

토큰 버킷이라는 지정된 용량을 갖는 컨테이너를 사용한 알고리즘이다.

동작원리

  • 토큰 버킷은 지정된 용량을 갖는 컨테이너
  • 토큰 공급기는 사전 설정된 양의 토큰을 주기적으로 추가하고 버킷에 채워진다.
  • 토큰이 꽉 찬 버킷에는 더 이상의 토큰이 추가되지 않고 버려진다. (overflow)
  • 각 요청은 처리될 때 마다 하나의 토큰을 사용하여 요청이 도착하면 버킷에 충분한 토큰이 있는지 검사한다.
  • 충분한 토큰이 있는 경우, 버킷에서 토큰 하나를 꺼낸 후 요청을 시스템에 전달한다.
  • 충분한 토큰이 없는 경우, 해당 요청은 버려진다.(dropped)

장/단점

  • 장점
    • 구현이 쉽다.
    • 메모리 사용 측면에서도 효율적이다.
    • 짧은 시간에 집중되는 트래픽도 처리가 가능하다. 버킷에 남은 토큰이 존재만하면 요청은 시스템에 전달된다.
  • 단점
    • 버킷 크기토큰 공급률이라는 두 개 인자를 가지고 있는데, 이 값을 적절하게 튜닝하기 어렵다.
      • 버킷 크기 : 처리할 요청의 최대 수
      • 토큰 공급률 : 토큰이 추가되는 속도

누출 버킷 알고리즘 (fixed bucket)

토큰 버킷 알고리즘과 비슷하지만 요청 처리율이 고정되어 있다.
보통 FIFO(First In First Out)으로 구현한다.

동작원리

  • 요청이 도착하면 큐가 가득 차 있는지 확인하고 빈 자리가 있을 경우에는 큐에 요청을 추가한다.
  • 큐가 가득 차 있는 경우에는 새 요청은 버린다. (dropped)
  • 지정된 시간마다 큐에서 요청을 꺼내어 처리한다.

장/단점

  • 장점
    • 큐의 크기가 제한되어 있기 때문에 메모리 사용량 측면에서 효율적이다.
    • 고정된 처리율을 갖고 있기 때문에 안정적 출력이 필요한 경우 적합하다.
  • 단점
    • 단 시간에 많은 트래픽이 몰리는 경우 큐에는 오래된 요청들이 쌓이게 되고, 그 요청들을 제때 처리하지 못하면 최신 요청들은 버려지게 된다.
    • 버킷크기처리율 갖는데, 이들을 올바르게 튜닝하기 어렵다.
      • 버킷 크기 : 큐의 사이즈
      • 처리율 : 단위 시간당 처리되는 작업의 양

고정 윈도 카운터 알고리즘 (fixed windwon counter)

고정 윈도 카운터 알고리즘은 정해진 시간 구간 동안 허용 임계값 만큼의 요청만 처리하고, 임계값 이상의 요청들은 새로운 시간구간이 찾아올 때 까지 버리는 방식이다.

동작원리

  • 타임라인을 고정된 간격의 윈도로 나누고, 각 윈도마다 카운터를 붙인다.
  • 요청이 접수될 때 마다 이 카운터의 값은 1씩 증가한다.
  • 이 카운터의 값이 사전에 설정된 임계치에 도달하면 새로운 요청은 새 윈도가 열릴 때 까지 버려진다.

장/단점

  • 장점
    • 메모리 효율이 좋다.
    • 이해하기 쉽다.
    • 윈도우가 닫히는 시점에 카운터를 초기화하는 방식은 특정한 트래픽 패턴을 처리하기에 적합하다.
  • 단점
    • 윈도의 경계 부근에 순간적으로 많은 트랙픽이 집중될 경우에 윈도에 할당된 양보다 더 많은 요청이 처리될 수 있다.

이동 윈도 로깅 알고리즘 (sliding window log)

이동 윈도 로그 알고리즘은 고정 윈도 카운터 알고리즘에서 지적한 단점을 보완하기 위해 등장한 알고리즘이다.

동작원리

  • 요청의 타임스탬프를 추적한다. 타임스탬프 데이터는 보통 Redis의 정렬 집합(Sorted set)같은 캐시에 보관한다.
  • 새 요청이 오면 만료된 타임스탬프는 제거한다.
    • 만료된 스탬프의 의미는 그 값이 현재 윈도의 시작 시점보다 오래된 타임스탬프를 말한다.
  • 새 요청의 타임스탬프를 로그에 추가한다.
  • 로그의 크기가 허용치보다 같거나 작으면 요청을 시스템에 전달한다. 그렇지 않은 경우에는 처리를 거부한다.

장/단점

  • 장점
    • 정교한 매커니즘
    • 어느 순간의 윈도를 보더라도, 허용되는 요청의 개수는 시스템의 처리율 한도를 넘지 않는다.
  • 단점
    • 거부된 요청의 타임스탬프도 보관하기 때문에 다량의 메모리를 사용한다.

이동 윈도 카운터 알고리즘 (sliding window counter)

고정 윈도 카운터 알고리즘과 이동 윈도 로그 알고리즘을 결합한 것이다.

동작원리

장/단점

  • 장점
    • 이전 시간대의 평균 처리율에 따라 현재 윈도의 상태를 계산하므로 짧은 시간에 몰리는 트래픽에도 대응하는데 좋다.
    • 메모리 효율이 좋다.
  • 단점
    • 직전 시간대에도 도착한 요청이 균등하게 분포되어 있다고 가정한 상태에서 추정치를 계산하기 때문에 다소 느슨하다.

스프링부트에서 사용가능한 라이브러리 종류

이번에는 SpringBoot에서 처리율 제한 기능을 구현하기 위한 대표적인 라이브러리에 대해서 정리해보려고한다.

1. Bucket4j

Bucket4j는 토큰 버킷 알고리즘을 기반으로 하는 Java 속도 제한 라이브러이다.

특징으로는 동시에 lock-free한 구현으로 멀티 스레딩 환경에서의 확장성이 우수하며, 추가적인 동시성 전략도 제공한다.

2. Guava

Guava는 구글에서 개발한 라이브러리이며, 토큰 버킷 알고리즘 기반이다.

Guava의 경우 처리율 제한 목적으로 나온 라이브러리는 아니지만, 제공하는 기능 중 처리율 제한 기능이 존재한다.

3. RateLimitJ

RateLimitJ는 이동 윈도 알고리즘을 기반으로 하는 Java 속도 제한 라이브러리이다.

Github ReadMe 첫 줄에 "This project is no longer supported, please consider using Bucket4j instead" 라고 나와 있다...

어떤 알고리즘 ? 어떤 라이브러리 ?

여러 처리율 제한 알고리즘, 어떠한 라이브러리를 사용하지에 대해 고민해보았다.

우선, 진행하는 프로젝트에서 처리율 제한 장치를 왜 도입하는지에 대해 생각해보았다.
도입하는 이유는 구글 STMP 서비스 제한된 사용량에 따적 악의적인 많은 요청을 예방하기 위한 것이다.

위 요구사항을 만족하기 위해서는, 어떤 처리율 알고리즘을 사용해도 무방할 것이라고 생각이 들었다.

그래서 해당 프로젝트에서 사용할 수 있는 라이브러리를 기반으로 생각해보았다.

진행중인 프로젝트는 3.2.5버전의 Spring Boot 프레임워크로 진행하고 있다.

하지만, RateLimitJ는 현재 더 이상 지원하지 않기때문에 Bucket4J를 사용하라는 문구가 적혀 있으며, 이슈가 발생할 수 있다는 점을 고려했을 때, 후보에서 제외하였다.

남은 GuavaBucket4J 중 고민을 했다.
두 라이브러리 모두 토큰 버킷 알고리즘을 기반으로 하고 있는데 어떤 차이점이 있는지 알아보았다.
Guava는 구글에서 개발한 Java 라이브러리로, 원래 목적으로는 처리율 제한 목적이 아니고 기본적인 토큰 버킷 알고리즘을 제공한다고한다. 그에 비해 Bucket4J는 토큰 생성 속도 조절, 토큰의 최대 용량 설정, 동적 조절 등 다양한 기능을 제공한다고 한다.

따라서, Bucket4J는 Guava에 비해서 더 복잡한 요청 제한을 구현하기 유용하다고 판단해서 토큰 기반 알고리즘 기반의 Bucket4J를 사용하기로 결정했습니다.

profile
Back-End Developer

0개의 댓글

관련 채용 정보