[대규모 시스템 설계 기초] 4장. 처리율 제한 장치의설계 - 3

상세설계
처리율 제한이란?
처리율 제한(Rate Limiting)은 사용자가 일정 시간 동안 API를 호출할 수 있는 최대 요청 수를 제한하는것을 의미합니다.
특정 사용자의 과도한 요청으로 인한 서버 과부하 방지를 막아줍니다.
처리율 제한 규칙
Lyft 는 처리율 제한에 오픈소스로 사용되고 있습니다. Envoy Proxy와 함께 사용하면 분산 환경에서도 중앙 집중적으로 요청 제한을 관리할 수 있습니다.
Lyft는 마이크로서비스 아키텍처에서 이 Ratelimit 서비스를 광범위하게 활용하고 있습니다.
공식 깃허브: https://github.com/envoyproxy/ratelimit
GitHub - envoyproxy/ratelimit: Go/gRPC service designed to enable generic rate limit scenarios from different types of applicati
Go/gRPC service designed to enable generic rate limit scenarios from different types of applications. - envoyproxy/ratelimit
github.com
요청은 아래 순서로 흐릅니다.
Client → Envoy Proxy → Ratelimit Service → Redis
- Envoy가 클라이언트의 요청을 먼저 받아, Ratelimit 서비스에 요청 허용 여부를 질의합니다.
- Ratelimit 서비스는 Redis에 저장된 카운터를 확인하고 초과했다면 OVER_LIMIT 응답을 Envoy에 돌려줍니다.
- Envoy는 그 결과에 따라 200 OK 또는 429 Too Many Requests를 반환합니다.
Redis를 이용한 카운터 관리
Ratelimit 서비스는 Redis를 전역 카운터 저장소로 사용합니다.
예를 들어 다음과 같이 user_id를 키로 두고 minute 단위 요청 수를 관리할 수 있습니다.
사용자 1명이 1분에 5개의 메시지만 보낼 수 있도록 제한합니다.
domain: messaging_service
descriptors:
- key: user_id
rate_limit:
unit: minute
requests_per_unit: 5
처리율 한도 초과 트래픽의 처리
클라이언트 요청이 처리율 제한(Rate Limit)을 초과하면 서버는 HTTP 429 (Too Many Requests) 응답을 반환합니다. 이 응답은 현재 요청이 너무 많아 일시적으로 처리할 수 없다는 의미로, 클라이언트는 잠시 대기 후 재시도해야 합니다.
일부 시스템은 초과된 요청을 바로 폐기하지 않고, 큐(Queue)에 보관하여 나중에 순차적으로 처리하거나 재시도하도록 구성할 수도 있습니다.
처리율 제한 장치가 사용하는 HTTP 헤더
서버(또는 Envoy / API Gateway)는 클라이언트가 제한 상태를 파악할 수 있도록 HTTP 응답 헤더에 관련 정보를 포함시킵니다.
- X-Ratelimit-Remaining: 윈도 내에 남은 처리 가능 요청의 수
- X-Ratelimit-Limit : 매 윈도마다 클라이언트가 전송할 수 있는 요청의 수
- X-Ratelimit-Retry-After(or Reset) : 한도 제한에 걸리지 않으려면 몇 초 뒤에 요청을 다시 보내야 하는지 알림
현재 요청 제한을 초과했으니 60초 후에 다시 요청의 예시입니다.
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
X-RateLimit-Limit: 5
X-RateLimit-Remaining: 0
Retry-After: 60
{
"message": "Rate limit exceeded. Try again in 60 seconds."
}
클라이언트 재시도 전략
Rate Limit을 초과했을 때 단순히 재요청을 반복하면 서버 부하가 악화됩니다.
이를 방지하기 위해 지수 백오프(Exponential Backoff) 또는 Retry-After 헤더 기반 재시도 정책을 적용하는 것이 좋습니다.
예시 전략:
- 첫 실패 시 1초 후 재시도
- 두 번째 실패 시 2초, 세 번째 4초 … 이런 식으로 대기 시간을 2배씩 증가
- Retry-After 헤더가 있으면 그 값을 우선적으로 사용
상세설계
클라이언트가 서버에 요청을 보내면, 요청은 먼저 처리율 제한 미들웨어(Rate Limit Middleware)에 도달합니다.
이 미들웨어는 요청을 바로 처리하지 않고, 먼저 허용 가능한 요청인지 확인하는 역할을 합니다.
RateLimit 미들웨어는 일반적으로 Redis 같은 캐시 스토리지에
요청 카운터와 마지막 요청의 타임스탬프(timestamp)를 저장합니다.
요청이 허용 범위 이내라면 → 실제 API 서버로 요청을 전달하고,
요청이 제한을 초과했다면 → HTTP 429 Too Many Requests 응답을 반환합니다.
분산 환경에서의 처리율 제한 장치의 구현
단일 서버에서는 단순한 카운터 증가로 처리가 가능하지만,
분산 환경(멀티 서버, 멀티 인스턴스, 수평 확장된 시스템)에서는
경쟁 조건(Concurrency Issue)이 쉽게 발생할 수 있습니다.
예를 들어, 여러 인스턴스가 동시에 같은 Redis 카운터를 읽고 쓴다면 서로가 증가된 값을 인식하지 못해 제한이 제대로 동작하지 않을 수 있습니다.
경쟁 조건
경쟁 조건(Race Condition)은 여러 프로세스나 스레드가 동시에 같은 자원(Redis key 등)에 접근하면서 예상치 못한 결과를 만드는 상황을 말합니다.
- 두 서버가 동시에 counter 값을 읽는다.
(둘 다 예를 들어 5를 읽었다고 가정) - 각 서버는 +1을 수행한 후 Redis에 다시 저장한다.
(서버 A: 6, 서버 B: 6) - 하지만 실제로는 2번 증가했어야 하므로, 최종 값이 7이 아니라 6으로 덮어씌워진다.
이렇게 되면 요청 횟수가 정확히 계산되지 않고,
Rate Limit 정책이 제대로 적용되지 않는 문제가 발생합니다.
병행성이 심한 환경에서 경쟁 조건 이슈가 발생될 수 있다.
요청이 수천 TPS 이상 발생하는 환경에서는 이런 카운터 덮어쓰기 현상이 빈번하게 발생할 수 있습니다.
두 개 요청을 처리하는 스레드가 각각 병렬로 counter 값을 읽었으면 그 둘 가운데 어느쪽도 변경된 값을 저장하지 않은 상태라고 해보겠습니다.
두 요청이 거의 동시에 Redis에서 counter 값을 읽고,각자 독립적으로 증가시킨 뒤 저장하면 결국 하나의 업데이트만 반영됩니다.
경쟁 조건에서 문제를 해결하기 위한 방법으로는 lock이 있습니다. 하지만, lock 자체는 시스템의 성능을 많이 떨어뜨리게됩니다.
성능 최적화
데이터센터가 물리적으로 사용자와 멀리 떨어져 있을수록 요청 지연은 필연적으로 늘어납니다.
이를 줄이기 위해 대부분의 클라우드 사업자는 전 세계 여러 리전에 엣지(Edge) 서버를 배치해, 사용자에게 가장 가까운 위치에서 요청을 처리하도록 설계합니다.
데이터센터에서 멀리 떨어진 사용자를 지원하려면 보다 latency가 증가할 수밖에 없습니다. 사용자의 트래픽을 가장 가까운 에지 서버로 전달하여 지연시간을 줄이는것도 방법이 될 수 있습니다.
두번째로 고려해야 할것은 제한 장치 간에 데이터를 동기화할 때 최종 일관성 모델을 사용하는것입니다.
분산 환경에서는 여러 RateLimiter 인스턴스가 존재합니다.
한 리전에선 이미 한도를 초과했는데, 다른 리전은 아직 허용된 상태로 판단하면 사용자별 요청 제어가 깨질 수 있습니다.
이를 방지하기 위해 최종 일관성(Eventual Consistency) 을 보장하는 모델을 사용하는 것도 좋을거 같습니다.
모니터링
Rate Limit 시스템을 설치한 후에는 정상적으로 동작하고 있는지, 설정이 적절한지를 지속적으로 모니터링해야 합니다.
다음 두가지를 볼 필요가 있다.
- 채택된 처리율 제한 알고리즘이 효과적이다.
- 너무 완화되면 RateLimit의 의미가 없고, 너무 강하면 정상 요청까지 차단될 수 있습니다.
- 정의한 처리율 제한 규칙이 효과적이다.
- 실제 트래픽 패턴과 일치하는지, 요청 허용량이 너무 빡빡하지 않은지 검증 필요
트래픽이 일정하지 않고, 특정 시간대(예: 점심시간, 이벤트 오픈 등)에 몰린다면 정적인 규칙 대신 동적 Rate Limit 정책을 고려할 수 있을거 같습니다.
정리
Rate Limit 시스템의 핵심은
“요청을 막는 것”이 아니라 서비스를 안정적으로 운영하기 위한 트래픽 제어 밸런스를 잡는 것.
- 요청 개수는 임계치를 넘을 수 없다.
- 요청 개수는 잠시 동안 임계치를 넘을 수 있다.
여러 계층에서 처리율 제한을 할 수 있다.
- iptables 또는 방화벽 규칙을 사용하면, 특정 IP 주소 단위로 요청 수를 제한할 수 있습니다.
# 예: IP당 초당 10개의 연결만 허용
iptables -A INPUT -p tcp --dport 80 -m limit --limit 10/second --limit-burst 20 -j ACCEPT
이 방식은 커널 레벨에서 동작하기 때문에 가장 빠른 차단 레이어지만, 애플리케이션의 세부 정책(예: user_id 단위 제한)을 적용하기 어렵습니다.
애플리케이션 레벨에서는 서버 애플리케이션 내부나 Gateway (예: Envoy, Nginx, API Gateway)에서 요청별로 Rate Limit 정책을 정의하고, Redis 같은 외부 저장소로 카운트를 관리합니다. 이 방법은 유연하고 정책 중심적으로 설계할 수 있는 장점이 있습니다.
클라이언트 레벨에서는 캐시를 사용하여 API 호출 횟수를 줄이면 된다. 혹은 동일한 요청이 반복되지 않도록 브라우저나 앱 단에서 요청 빈도 제어도 방법이 될수있을거 같다.
Rate Limit에 걸려 요청이 실패했을 때, 클라이언트는 무작정 재요청을 반복하면 안 됩니다. 그럴 경우 서버 부하가 오히려 폭발적으로 증가할 수 있습니다. 재시도 로직을 구현할 때는 백오프 시간을 둔다.
'IT' 카테고리의 다른 글
| Spring AOP로 Controller 파라미터 자동 주입하기 (0) | 2025.11.16 |
|---|---|
| [대규모 시스템 설계 기초] 10장. 알림 시스템 설계 (0) | 2025.11.09 |
| [대규모 시스템 설계 기초] 4장. 처리율 제한 장치의설계 - 2 (0) | 2025.10.19 |
| [대규모 시스템 설계 기초] 4장. 처리율 제한 장치의 설계 - 1 (0) | 2025.10.19 |
| 가상 면접 사례로 배우는 대규모 시스템 설계 기초 1장 - 사용자 수에 따른 규모 확장성 (1) (0) | 2025.09.22 |
댓글
이 글 공유하기
다른 글
-
Spring AOP로 Controller 파라미터 자동 주입하기
Spring AOP로 Controller 파라미터 자동 주입하기
2025.11.16 -
[대규모 시스템 설계 기초] 10장. 알림 시스템 설계
[대규모 시스템 설계 기초] 10장. 알림 시스템 설계
2025.11.09 -
[대규모 시스템 설계 기초] 4장. 처리율 제한 장치의설계 - 2
[대규모 시스템 설계 기초] 4장. 처리율 제한 장치의설계 - 2
2025.10.19 -
[대규모 시스템 설계 기초] 4장. 처리율 제한 장치의 설계 - 1
[대규모 시스템 설계 기초] 4장. 처리율 제한 장치의 설계 - 1
2025.10.19