"가상 면접 사례로 배우는 대규모 시스템 설계 기초"를 읽고 정리한 글입니다 :)
처리율 제한 장치(Rate Limiter)는 클라이언트 혹은 서비스가 보내는 트래픽의 처리율(Rate)을 제어하기 위한 장치이다. 클라이언트의 요청 횟수가 제한 장치의 임계치(Threshold)를 넘어서면 추가로 도달한 모든 호출은 처리가 중단된다.
처리율 제한 장치를 사용하여 얻는 이점은 다음과 같다.
처리율 제한 장치를 설계할 때 다양한 것들을 고려해야 하는데, 이 책에서는 다음과 같은 사항들을 설명한다.
클라이언트에서 처리율 제한을 거는 것은 변조가 매우 쉽기에 안전하지 않다. 그래서 처리율 제한을 서버에서 수행하거나 혹은 별개의 미들웨어를 만들어 API 서버로 가는 요청을 통제하도록 할 수 있다. 별개의 미들웨어로 분리하는 경우 Third-Party API Gateway를 사용할 수도 있을 것이다.
처리율을 제한하는 알고리즘은 여러가지가 있고, 널리 알려진 알고리즘으로는 다음과 같은 것들이 있다.
이러한 알고리즘들의 기본 아이디어는 단순하다. 요청의 횟수를 추적할 수 있는 카운터를 두고, 이 카운터의 값이 지정한 한도를 넘어서면 그 이후의 요청은 거부하는 것이다. 여기서 카운터를 보관하는 위치는 일반적으로 메모리상에서 동작하는 캐시가 좋을 것이다. 디스크 접근을 기반으로하는 데이터베이스는 느리기 때문에, 레디스와 같은 메모리 기반의 저장장치를 사용한다.
어떠한 요청이 한도 제한에 걸리면, API는 429 응답 코드(too many requests)를 클라이언트에게 보낸다. 또한, 이 처리율 제한과 관련된 헤더를 같이 보내주는데, 이는 다음과 같다.
분산 환경에서 처리율 제한 장치를 설계하는 것은 꽤나 어려운 문제이다. 이 경우 경쟁 조건(Race Condition)과 동기화(Synchronization) 문제를 해결해야 한다.
경쟁 조건을 해결하는 가장 간단한 방법은 Lock일 것이지만, 이 경우 시스템의 성능이 상당히 떨어진다. 그래서 이 대신 루아 스크립트(Lua Script) 또는 Redis의 Sorted Set을 사용할 수 있을 것이다.
동기화 문제 역시 분산 환경에서 고려해야 할 요소이다. 대규모 시스템에서는 여러 대의 처리율 제한 장치 서버가 있을 것이다. 이때 한 클라이언트의 요청들이 각기 다른 제한 장치 서버에 도달한다면, 동기화 없이는 제대로 카운팅이 불가능 할 것이다. 이를 해결하기 위해 고정 세션(Sticky Session)을 사용하여 같은 클라이언트의 요청을 항상 같은 제한 장치로 보낼 수도 있다. 하지만, 이 방법은 규모면에서 확장 가능하지도 않고 유연하지도 않기에 추천되지 않는다. 더 좋은 해결책은 Redis와 같은 중앙 집중형 데이터 저장소를 사용하는 것이다.
앞에서 설명한 내용들 말고도 책에서 언급한 다른 고려사항들이 있다.