이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.

  • Backpressure, rate limit, queue limit, circuit breaker가 각각 어떤 장애를 막는가?
  • 429, 503, Retry-After를 client와 API 계약에서 어떻게 설계하는가?
  • 사용자·tenant·route별 제한을 인프라와 앱 중 어디서 적용해야 하는가?

개요

Backpressure는 시스템이 감당할 수 없는 일을 더 받지 않거나 천천히 받도록 upstream에 신호를 보내는 설계다. Rate limit은 그중 요청 속도를 제한하는 대표 수단이다. 제대로 설계하면 일부 요청을 일찍 실패시켜 전체 장애를 막고, 잘못 설계하면 정상 사용자 전체를 차단하거나 retry storm을 만든다.

  • Rate limit은 user, tenant, API key, IP, route 단위로 설계할 수 있다.
  • Queue limit은 내부 대기열이 무한히 커지는 것을 막는다.
  • Circuit breaker는 실패하는 downstream 호출을 잠시 차단해 연쇄 장애를 줄인다.
  • 429 Too Many RequestsRetry-After는 client가 회복 가능하게 행동하도록 돕는다.

원리

무제한 queue는 친절해 보이지만 장애를 키운다.

traffic spike
  -> queue grows
  -> latency grows
  -> client timeout
  -> retry increases
  -> queue grows more

backpressure는 이 루프를 끊기 위해 “지금은 못 받는다”를 빠르게 표현한다. 거절은 실패지만, 느리게 모두 실패하는 것보다 낫다.

정책별 역할

수단목적대표 응답
Rate limit속도 제한429
Concurrency limit동시에 처리 중인 작업 제한429/503
Queue limit대기열 폭증 방지503
Circuit breaker실패 downstream 보호503/fallback
Bulkhead장애 격리endpoint/tenant별 자원 분리
Load shedding우선순위 낮은 요청 제거429/503

Rate Limit Key 설계

기준장점함정
IP구현 쉬움NAT/VPN 사용자 전체 차단
User ID공정성 좋음인증 전 요청에는 적용 어려움
TenantB2B에 적합큰 tenant 내부 사용자 차이 숨김
API Key파트너 API에 적합key 공유/유출 대응 필요
Route비싼 API 보호전체 사용자 경험과 균형 필요

기업 운영에서는 무료/유료/내부/파트너 traffic class를 나누는 것이 중요하다. 개인 프로젝트도 로그인, 검색, 주문 같은 비싼 endpoint는 더 보수적으로 둘 수 있다.

코드와 설정 예시

NGINX rate limit 예시다.

limit_req_zone $binary_remote_addr zone=per_ip:10m rate=10r/s;
 
server {
    location /api/ {
        limit_req zone=per_ip burst=20 nodelay;
        proxy_pass http://app_upstream;
    }
}

앱에서 429를 반환할 때는 재시도 힌트를 준다.

HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/problem+json
 
{"type":"https://api.example.com/problems/rate-limit","detail":"retry after 30 seconds"}

Spring에서 간단히 표현하면 다음처럼 시작할 수 있다.

return ResponseEntity.status(429)
    .header(HttpHeaders.RETRY_AFTER, "30")
    .body(problem);

Retry와의 관계

Rate limit만 있고 client retry 가이드가 없으면 retry storm이 생긴다.

  • 429는 즉시 재시도하지 않는다.
  • Retry-After를 존중한다.
  • exponential backoff와 jitter를 사용한다.
  • non-idempotent 요청은 idempotency key 없이 자동 재시도하지 않는다.
  • client timeout이 너무 짧으면 서버는 처리 중인데 client가 재시도할 수 있다.

인프라 협업 포인트

  • Gateway/WAF/LB에서 제한할지 app에서 제한할지 역할을 나눈다.
  • IP 기준 제한은 NAT, mobile carrier, 사내망 사용자를 고려한다.
  • tenant별 quota와 burst 허용량은 제품/영업/운영 정책과 연결된다.
  • 429/503 metric, rejected count, queue length, circuit open count를 공통 dashboard로 본다.

실전 팁

  • 중요한 요청과 덜 중요한 요청을 같은 queue에 넣지 않는다. Bulkhead가 없으면 낮은 우선순위 작업이 핵심 기능을 막는다.
  • rate limit은 “조용히 실패”하면 안 된다. 응답 body와 header로 client가 다음 행동을 알 수 있어야 한다.
  • queue length는 latency보다 선행 지표다. queue가 늘기 시작하면 곧 p99가 무너진다.
  • 장애 중에는 추천/검색 자동완성/이미지 변환 같은 부가 기능을 먼저 shed하는 전략이 유용하다.
  • 개인 프로젝트에서도 로그인 시도, 이메일 발송, 파일 업로드에는 간단한 limit을 둔다.

위험 신호!

  • 모든 요청을 무한 queue에 넣는다.
  • 429에 Retry-After나 client 가이드가 없다.
  • IP rate limit 하나로 모든 사용자를 제한한다.
  • circuit breaker가 열렸는지 metric이 없다.

확인 질문

확인 질문

  • Backpressure가 필요한 이유는 무엇인가?
    • 과부하 상태에서 모든 요청을 오래 붙잡아 전체 장애를 키우지 않고, 감당 가능한 만큼만 처리하기 위해서다.
  • 429와 503은 어떻게 구분할 수 있는가?
    • 429는 client나 key가 허용량을 초과한 경우에 가깝고, 503은 서비스가 일시적으로 처리할 capacity가 부족한 경우에 가깝다.
  • retry storm을 줄이려면 무엇이 필요한가?
    • Retry-After, exponential backoff, jitter, idempotency key, 명확한 timeout 정책이 필요하다.

참고 문서