Request Rate Limiter
안녕하세요. 이번에는 Api Request Rate Limiter 대해 알아보고자 합니다.
일단 Rate Limiter 가 왜 필요한지 먼저 생각 해보자면 무분별한 DDoS 공격 방지에 효과적 이기 때문입니다. 특정 시간 내에 요청 수를 제한 하므로써
보다 안전한 서버를 유지 및 관리 할 수 있기 때문입니다.
Rate Limiter 알고리즘 으로써 대표적으로 ‘Token Bucket’, ‘Leaky Bucket’, ‘Fixed Window Rate Limiter’, ‘Sliding Window Rate Limiter’ 등이 있습니다.
여기서는 ‘Fixed Window Rate Limiter’, ‘Sliding Window Rate Limiter’ 중점으로 알아보고자 합니다.
Fixed Window Rate Limiter
‘Rate Limiter’ 사전적 의미로는 ‘비율 계산기’ 로 시스템의 안정성 또는 보안의 목적으로 요청의 수를 제한 하는 기술 입니다.
‘Fixed Window Rate Limiter’ 경우는 고정된 시간 안에 요청 수를 제한 하는 방법 입니다.
예를들어 1분 안에 요청 수를 10번 만 제한 해서 10번 이상 요청이 들어오면 ‘429 Too Many Requests’ 응답을 내리도록 합니다.
0.0.0.0 IP 가지고 있는 사람이 9시 30분에 요청을 했다고 하면 IP 0.0.0.0 가지고 있는 사용자는 30분에 2번만 방문을 했으니
‘GET 0.0.0.0:30’ Redis 서버로 명령어를 입력하면 ‘2’ 로 반환하게 됩니다.
아직 분 단위로 10번 이상 방문하지 않았으므로 호출을 통과 합니다.
이후에는 Redis 서버에 해당 IP 가 호출 횟수를 저장 해야 합니다. 저장 하기전 Redis 원자성을 보장 하기 위해 ‘MULT’ 명령어 통해 트렌젝션을 열리도록 합니다.
Redis 의 데이터 타입 중 가장 기본적인 String 데이터 타입을 이용해서
‘INCR 0.0.0.0:30’ 명령어를 통해 1씩 증가 하도록 합니다. 숫자형 String 값으로 INCR 명령어를 이용하면 1씩 증가 됩니다.
동시에 ‘EXPIRE 1.1.1.1:30 3600’ 명령어를 통해 ‘1.1.1.1:30’ 이라는 키값을 유효기간을 설정 합니다. (초단위)
입력하고자 하는 명령어 입력이 완료되면 트렌젝션을 완료 시키기 위해 입력했던 명령어를 실행하기 위한 ‘EXEC’ 명령어를 입력 합니다.
그런데 만약 분단위로 10번 이상 호출 하게 된다면 ‘429 Too Many Requests’ 응답을 내리도록 서버에서 핸드링 하도록 합니다.
‘Fixed Window Rate Limiter’ 경우에는 구현하기에 있어서 쉽다는 장점이 있습니다. 하지만 단점이 있는데요. 말그대로 Fixed 고정된 분단위로
방문 수를 측정 하다보니 예를들어서 10분 59초에 갑자기 9번 호출 하고 11분 01초에 9번 호출 하게 된다면 10분 59초 ~ 11분 01초 사이에는 18번 호출
하게 되면서 두배의 부하를 받을 수 있다는 단점이 있습니다. 왜냐하면 11분이 되면 다시 조회수 카운팅 초기화가 되기 때문 입니다.
Sliding Window Rate Limiter
앞써 보았던 ‘Fixed Window Rate Limiter’ 다르게 ‘Sliding Window Rate Limiter’ 는 시간에 따라 윈도우의 간격을 이동시켜서 동적으로 요청 수를 조절 합니다.
‘Fixed Window Rate Limiter’ 단점인 window 경계 시간대에 요청이 몰리면 두배의 부하가 되는 문제를 해결 할 수 있습니다.
즉 세밀한 방식으로 요청수 제한을 할 수 있습니다.
예를들어 ‘Fixed Window Rate Limiter’ 경우 31분이 되면 요청 허용수가 다시 초기화가 되지만 ‘Sliding Window Rate Limiter’ 경우에는
현재 시간으로부터 60초 전까지의 데이터를 확인하기 때문에 계속해서 지난 1분간의 요청 횟수를 카운팅 할 수 있습니다.
‘Sliding Window Rate Limiter’ 경우에는 Redis 데이터 타입 중 ‘Sorted Set’ 이용해서 구현이 가능 합니다.
Sorted Set 의 명령어 중에 score 대신 초단위로 변경한 Unix time 으로 변환해 구현 합니다.
09시 30분에 IP 0.0.0.0 인 사용자가 요청이 들어오면 요청 시간을 Unix Time (1732008600) 으로 변환 합니다.
09시 29분 ~ 09시 30분 사이에 요청수를 계산하기 위해서는 우선 요청 시간에 들어왔던 시간 09시 30분에서 1분을 뺀 09시 29분의 Unix Time (1732008540)
을 변환 합니다. 그런 후 Redis 명령어 중 ‘ZREMRANGEBYSCORE 0.0.0.0 0 1732008540’ 이용해서 0 부터 09시 29분 까지 ‘0.0.0.0’ 이라는 key 값으로 초기화 합니다.
그런 후 ZCARD 명령어를 이용해서 ‘ZCARD 0.0.0.0’ 통해 09시 29분 ~ 09시 30분 사이의 요청 수를 가져올 수 있게 됩니다.
반환된 값이 10 횟수 이상이 아니면 ‘ZCARD 0.0.0.0 1732008600 1732008600’ 입력 해서 score (접속한 unix time 시간) 및 member (접속한 unix time 시간) 를 저장 합니다.
Copyright 201- syh8088. 무단 전재 및 재배포 금지. 출처 표기 시 인용 가능.