본문 바로가기
C.W.K.
Stream
Lesson 06 of 10 · published

속도 제한과 역압

~12 min · production, rate-limits

Level 0수련생
0 XP0/100 lessons0/14 achievements
0/120 XP to next level120 XP to go0% complete

제공업체의 제한은 곧 우리 서비스의 제한이야

제공업체는 분당 요청 수(RPM), 분당 토큰 수(TPM), 동시 요청 수라는 세 층에서 속도를 제한해. 애플리케이션은 이 경계를 넘지 않도록 요청을 조절해야 해. 그래도 제한에 걸리면 매끄럽게 복구하고, 요청이 안쪽에 끝없이 쌓이지 않도록 호출자에게 역압을 전달해야 하지.

실전 전술

  • 요청을 고르게 흘려보내기 — 나가는 길목에 토큰 버킷이나 누출 버킷을 두면 순간적인 폭주가 곧바로 429로 이어지지 않아.
  • 무작위 지연을 섞은 재시도 — 지수 백오프에 무작위 시간을 더하고, 최대 시도 횟수도 정해.
  • 사용자별 제한 — 한 클라이언트의 폭주가 모두를 막지 않게 하고, 사용자가 자신의 한도를 화면에서 볼 수 있게 해.
  • 역압 — 뒷단이 과부하면 호출자에게 429를 돌려줘. 아무 말 없이 내부 대기열에 쌓아두면 안 돼.
  • 등급별 분기 — 사용량이 많은 사용자는 상위 등급 API 키로, 적은 사용자는 공동 풀로 보내.

사용자에게도 상황을 보여줘

  • “속도를 줄여줘. 30초 뒤에 다시 시도해”처럼 다음 행동을 알려주는 429 응답.
  • 유료 고객이 자신의 할당량을 확인할 수 있는 현황판.
  • 한도에 가까워졌을 때 나타나는 비활성 버튼이나 안내 배너.

Code

나가는 요청의 토큰 버킷·python
from anyio import sleep
from asyncio import Lock
from time import monotonic

class TokenBucket:
    def __init__(self, rate_per_sec: float, capacity: int):
        self.rate, self.capacity = rate_per_sec, capacity
        self.tokens = capacity
        self.last = monotonic()
        self.lock = Lock()

    async def take(self, n: int = 1):
        async with self.lock:
            while True:
                now = monotonic()
                self.tokens = min(self.capacity, self.tokens + (now - self.last) * self.rate)
                self.last = now
                if self.tokens >= n:
                    self.tokens -= n
                    return
                await sleep((n - self.tokens) / self.rate)

External links

Exercise

클라이언트 하나에 토큰 버킷 속도 조절기를 붙여봐. 제공업체 RPM의 열 배 속도로 요청을 쏘아 부하 시험을 해. 제공업체까지 429가 넘어가지 않는지, 클라이언트가 갑작스러운 실패 대신 일정한 지연을 겪는지 확인해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.