-
Rate limit에 관해글또 2023. 3. 4. 20:26
안녕하세요.
이번에는 Rate Limit에 대해 알아보겠습니다.
이번 글을 쓰게 된 계기는 일부 코드에서 bucket4j를 사용하는 코드를 보았는데 흥미로웠습니다.
이들이 사용된 위치는 서버의 자원을 많이 잡아먹는 API였습니다. 예를 들면, PDF 생성 등이죠.
Print server를 만들 수도 있지만, 어드민 전용일 경우 단순 요청 제한을 걸어둔 것입니다.
대규모 서비스에서도 Rate Limit를 쉽게 찾아볼 수 있습니다. 슬랙의 경우 아래와 같은 정책을 가지고 있습니다.
Rate Limit이란 무엇인가요?
웹사이트, API 또는 서버에서 사용자의 요청 속도를 제한하는 것입니다. 이는 서버에 대한 부하를 줄이고 서비스의 가용성과 안정성을 유지하기 위해 사용됩니다. 일반적으로는 초, 분 또는 시간당 요청 수 등의 제한을 설정하여 초과하면 오류나 차단 등을 합니다.
왜 필요할까요?
앞에서 말했듯 서버에는 일정 처리량이 존재하고 이를 넘어서면 과부하 상태가 되어 성능 저하나 다운이 될 수 있습니다.
따라서 안정성과 신뢰성을 보장하고 악성 사용자로부터 서비스를 보호하기 위해 Rate Limit을 설정합니다. 또한, 클라이언트에서도 요청을 제한하는 로직을 구현하여 사고를 방지할 수 있습니다
구현 알고리즘
Rate Limit을 구현하는 방법에는 여러 가지가 있습니다.
대표적인 알고리즘으로는 다음과 같은 것들이 있습니다.
1. Token Bucket Algorithm
양동이에 토큰을 담아두고 요청마다 하나씩 소비하는 방식입니다. 다 소진하면 요청을 거부하는 것이지요.
일정 시간 뒤 다시 양동이에 토큰을 다시 Refill 합니다.
유명한 java 라이브러리
2. Leaky Bucket Algorithm
트래픽을 일정한 속도로 내보내는 알고리즘입니다. 양동이는 구멍이 뚫려 있어 일정량으로 처리가능하며
양동이가 꽉 차면 요청을 거부합니다.
3. Fixed Window Counter
일정 시간 동안 API 호출 횟수를 제한하는 것입니다. 1분에 10번의 요청등으로 말이죠.
하지만 만약 10:42분 59초에 10개가 들어고 10:43분 01초에 10개가 더 들어올 경우 예상치 이상의 부하를 받을 수 있습니다.
이를 경계 편향문제라고 합니다.
4. Sliding Window Log
일정 시간 동안 API 호출 기록을 유지고 이를 토대로 API 호출 횟수를 제한하는 방식입니다.
3번은 일정 시간이 정해져 있다면 4번 알고리즘은 사용자 요청을 받은 시점부터 계산하기에 호출 속도를 예측 가능합니다.
매번 API 호출 기록을 유지하다 보니 다른 알고리즘에 비해 메모리 사용량이 높습니다.
각자의 알고리즘에 대한 자세한 내용은 다음 위키를 참고하세요.
Rate limit는 어디에서 할까?
애플리케이션
애플리케이션에서 Rate limit를 구현할 수도 있습니다.
다만 굳이 네트워크단에서 거를 수 있는 요청을 애플리케이션까지 끌어들이는 이유는 여러 가지 장점이 있기 때문입니다.
사용자의 요청 수를 보고 계정을 정지시킨다던지, 적절한 에러 메시지도 커스텀할 수 있습니다.
네트워크
Rate limit를 거는 방법은 애플리케이션 단 만이 아닌 네트워크단에서 거를 수 있습니다.
로드밸런서에서 Rate limit를 걸 수 있습니다.
예로 nginx에서는 여러 알고리즘을 통해 제어할 수 있지만
leaky bucket 알고리즘을 이용해 적절한 양을 정할 수도 있습니다.
또는 서비스 업체에서 제공하는 제품을 사용할 수도 있습니다.
API Gateway, WAF(Web Application Firewall), Load Balancer 등의 서비스를 제공하며, 이를 이용해서 rate limit을 구현할 수 있습니다. 이 경우에는 클라우드 서비스 제공 업체가 자동으로 관리해 주므로 운영자의 부담이 줄어들 수 있습니다.
분산서버환경에서의 Rate limit
분산환경에서는 여러 클라이언트의 요청 수를 저장하고 공유하는 방법이 필요합니다.
로드밸런서가 Rate limit를 조회해 수락/거부를 결정해야 합니다.
글로벌 캐시
글로벌 캐시는 여러 노드 간에 공유되는 하나의 캐시입니다.
그렇기에 데이터의 일관성을 보장하면서도 각 노드에서 공통적으로 접근할 수 있습니다. 하지만, 글로벌 캐시의 경우, 네트워크 지연 등으로 인해 성능이 저하될 가능성이 있고, 하나의 노드에 장애가 발생하면 전체 시스템에 영향을 미칠 수 있습니다.
분산 캐시
분산 캐싱은 여러 대의 서버에 걸쳐 캐시를 분산시키는 방법을 말합니다.
성능이 우수하며 장애 발생 시 영향 범위가 적습니다.
그러나, 데이터 일관성을 보장하기 위해 추가적인 동기화 작업이 필요하며, 이 작업이 성능에 영향을 미칠 수 있습니다.
대표적인 분산 캐시 솔루션 중 하나인 Redis에서는 클러스터링을 통해 여러 노드를 하나의 논리적인 그룹으로 묶어서 동작합니다.
Redis 클러스터에서는 각 노드가 서로 통신하면서 데이터를 동기화하고, 클러스터 내의 모든 노드가 동일한 데이터를 유지하도록 하는 것이지요.
또한, 분산 캐시를 구현하는 다른 방법으로는 캐시 노드 간에 캐시 데이터의 일관성을 유지하기 위해 캐시 무효화(invalidation) 메시지를 브로드캐스팅하는 방법이 있습니다. 이 방법은 각 노드가 자체적으로 캐시 데이터를 관리하면서, 무효화 메시지를 수신하면 해당 데이터를 무효화하고, 새로운 데이터를 가져와서 캐시 합니다. 이 방법은 데이터의 일관성을 유지하면서도 빠른 응답 속도를 제공할 수 있습니다.
어떤 방식이 더 유리한지는 사용하려는 시스템의 특성과 운영 환경에 따라 다를 수 있습니다. 일반적으로는 분산 캐싱을 사용하는 것이 더 성능이 우수하고 확장성이 높은 방식으로 평가되기도 합니다.
라인의 사례
+
TarPit
악성 유저의 요청의 리스폰스를 지연시키는 방법이다. 이를 통해 악성 유저의 리소스를 소모시키는 것이다.
스레드는 계속해서 사용 중 이기 때문에 악성유저와 일반유저를 잘 걸러서 적용해야 한다.
해당 방법을 통해 악성유저의 부하를 예방할 수 있다.
스레드를 계속해서 사용하는 것이 신규 스레드 생성이나 컨택스트 스위치로 인한 오버헤드보다 부하가 적다.
다만 유저의 사용자 경험을 떨어뜨릴 수 있다.
참고
'글또' 카테고리의 다른 글
제로페이로드 (0) 2023.03.25 트위터 시스템 디자인 (0) 2023.03.11 코틀린 (0) 2023.02.24 문제의 본질을 파악하는 방법 (0) 2023.02.16 캐시컨트롤에 대해 (0) 2023.02.13