네트워크 시스템에서 처리율 제한 장치는 클라이언트 또는 서비스가 보내는 트래픽의 처리율을 제어하기 위한 장치입니다. 아래에 나열된 사례를 보면, 더욱 이해가 쉬울 것입니다.
API를 처리할 때 이러한 처리율 제한 장치를 왜 두는 것일까요?
이 처리율 제한 장치는 클라이언트 측에 둘 수도 있고, 서버 측에 둘 수도 있습니다. 그러나 클라이언트의 요청은 쉽게 위변조가 가능해 처리율 제한을 안정적으로 수행할 수 있는 장소로는 부적합합니다. 따라서 서버에 두는 방식을 고려해 볼 수 있습니다.
다른 방법을 말해 보자면, 처리율 제한 장치를 API 서버에 두는 대신, 처리율 제한 미들웨어를 만들어서 책임을 분리하는 것입니다.
토큰 버킷은 지정된 용량을 가지는 컨테이너입니다. 이 버킷에는 사전에 설정된 양의 토큰이 주기적으로 채워집니다. 하나의 요청은 하나의 토큰 컨테이너 위에 올려지게 됩니다. 따라서 요청이 도착했을 때, 사용할 수 있는 토큰이 있는 경우에만 토큰을 꺼내서 사용하고, 그렇지 않을 경우 요청은 버려지게 됩니다. 통상적으로 API 엔드포인트마다 별도의 버킷을 두어서, 기능마다 요청 허용 범위를 다르게 설정합니다.
❕토큰 버킷 알고리즘 인자
❕토큰 버킷 알고리즘 장점
❕토큰 버킷 알고리즘 단점
누출 버킷 알고리즘은 요청 처리율이 고정되어 있는 알고리즘입니다. 보통 FIFO 큐로 구현합니다. 요청이 도착했을 때 큐가 가득 차 있으면 요청을 버리고, 아니라면 대기 큐에 요청을 추가합니다.
❕누출 버킷 알고리즘 인자
❕누출 버킷 알고리즘 장점
❕누출 버킷 알고리즘 단점
고정 윈도 알고리즘은 타임라인을 고정된 간격의 윈도로 나누고, 각 윈도마다 카운터를 붙입니다. 요청이 접수될 때마다 카운터가 하나씩 증가합니다. 이 카운터가 임게치까지 도달하면 새로운 요청은 새로운 윈도우가 열리는 타임라인이 될 때까지 접수되지 않습니다.
❕고정 윈도 버킷 알고리즘 인자
❕고정 윈도 버킷 알고리즘 장점
❕고정 윈도 버킷 알고리즘 단점
이중 윈도 로그 알고리즘은 고정 윈도 카운터의 단점을 해결하기 위한 방안으로, 요청의 타임스탬프를 추적하는 방법입니다. 타임스탬프는 보통 레디스와 같은 캐시에 보관합니다. 새 요청이 오면 만료된 타임스탬프는 제거하고, 새 요청의 타임스탬프를 기록합니다.. 로그의 크기가 허용치보다 같거나 작으면 해당 요청을 시스템에 전달합니다.
❕이중 윈도 로그 알고리즘 인자
❕이중 윈도 로그 알고리즘 장점
❕이중 윈도 로그 알고리즘 단점
이중 윈도 카운터 알고리즘은 고정 윈도 카운터 알고리즘과 이됭 윈도우 로깅 알고리즘을 결합한 것입니다. 현재 윈도우에 온 요청의 개수를 구하는 공식은 다음과 같습니다.
현재 1분간의 요청 수 + 직전 1분간의 요청 수 * 이동 윈도우와 직전 윈도우가 겹치는 비율
❕이중 윈도 카운터 알고리즘 인자
❕이중 윈도 카운터 알고리즘 장점
❕이중 윈도 카운터 알고리즘 단점
처리율 제한 규칙을 실행하기 위한 카운터의 값들은 보통 빠르게 읽어오기 위해서 레디스와 같은 메모리 기반 저장 장치로 구현합니다. 레디스에서는 두 가지 명령어를 지원합니다.
INCR
: 메모리에 지정된 카운터 값을 1만큼 증가시킵니다.EXPIRE
: 카운터에 타임아웃 값을 설정합니다.그렇다면 처리율 제한 규칙 알고리즘은 어디에 저장되어 있을까요? 보통 설정 파일에 디스크에 저장됩니다.
처리율 한도를 초과한 요청은 그대로 버려질 수도 있고, 메시지 큐에 보관될 수도 있습니다. 그렇다면 클라이언트는 자신의 요청이 처리율 제한에 걸리고 있는지를 어떻게 알 수 있을까요? 그 정답은 HTTP 응답 헤더에 있습니다. 처리율 제한 장치를 설계하면 아래와 같은 HTTP 응답 헤더를 클라이언트에게 전송하게 됩니다.
X-Ratelimit-Remaining
: 윈도우 내에 남은 처리 가능한 요청의 개수입니다.X-Ratelimit-Limit
: 매 윈도우마다 클라이언트가 전송할 수 있는 요청의 개수입니다.X-Ratelimit-Retry-After
: 한도 제한에 걸리지 않으려면 몇 초 뒤에 요청을 다시 보내야 하는지 알려 줍니다.분산 환경에서 처리율 제한 장치를 구현하려면 다음과 같은 두 가지 문제를 해결해야 합니다.
❕경쟁 조건
요청을 처리하는 여러 개의 스레드가 병렬로 카운터의 값을 증가시킬 경우, 카운터의 값이 제대로 반영되지 않을 수 있습니다. 경쟁 조건을 해결하는 대표적인 해결책은 락을 거는 것이지만, 이는 시스템의 성능을 떨어뜨린다는 문제점을 야기할 수 있습니다.
❕동기화 이슈
처리율 제한 장치를 여러 개 두면 동기화가 필요해집니다. 웹 계층은 무상태이므로 클라이언트는 요청을 각기 다른 제한 장치에 보내게 될 수도 있습니다. 제한 장치끼리 동기화되지 않는다면 클라이언트에 대한 카운터 값을 모르기 때문에 처리율 제한이 제대로 이루어지지 않을 수 있습니다.
이에 대한 해결책 중 하나는 고정 세션을 활용해서 한 클라이언트는 같은 처리율 제한 장치로만 요청을 보내도록 유도하는 것입니다. 하지만 이 방식은 규모면에서 확장 가능하지도 않고, 유연하지도 않기 때문에 추천하지 않습니다. 레디스와 같은 중앙 집중형 저장소를 쓰는 것이 더욱 적합한 방법이라고 볼 수 있겠습니다.