LabHub
배우기 러닝패스 코스

FastAPI — 타입이 곧 계약이다 · 요청 제한의 경계 시각을 검증한다 · 실습

요청 제한의 경계 시각을 검증한다

LabHub 에서 이어서 보기

목표

가상 시계로 슬라이딩 윈도와 Retry-After를 검사하고 사용자별 한도를 분리합니다.

왜 중요한가

트래픽이 늘자 서버는 모든 요청을 같은 목록에 기록하기 시작했다. 한 사용자의 연속 요청이 다른 사용자의 정상 요청까지 막았다. 창의 마지막 시각에서 항목을 지우는 비교 연산도 틀려 제한이 1초 더 유지됐다. 실제로 수십 초 기다리는 테스트는 이런 경계를 느리고 불안정하게 만든다.

단계

1. /root/work/fa-rate-window-lab/service.py에서 validate_limit(limit, window)는 bool 제외 양의 int limit와 양의 유한 int/float window만 허용해 (limit, float(window))를 반환합니다. 나머지는 ValueError입니다.

처음 한 번 준비하세요. 기존 파일은 덮어쓰지 않습니다.

mkdir -p /root/work/fa-rate-window-labtest -e /root/work/fa-rate-window-lab/service.py || cp /opt/fixtures/ten_labs/fa-rate-window-lab/service.py /root/work/fa-rate-window-lab/service.pycd /root/work/fa-rate-window-lab

2. /root/work/fa-rate-window-lab/service.py에서 active(history, now, window)는 now-window보다 큰 시각만 원래 순서의 새 리스트로 반환합니다. history는 정렬된 비감소 시각입니다.

3. /root/work/fa-rate-window-lab/service.py에서 retry_after(history, now, window)는 이미 정리된 비어 있지 않은 history의 첫 시각+window-now를 ceil한 값과 0 중 큰 정수입니다. 빈 리스트는 0입니다.

4. /root/work/fa-rate-window-lab/service.py에서 history_for(state, key)는 없는 키면 빈 리스트, 있으면 그 기록의 사본을 반환합니다. 조회만으로 state를 수정하지 않습니다.

5. /root/work/fa-rate-window-lab/service.py에서 admit(state, key, now, limit, window)는 설정 검증 후 해당 키의 만료 기록을 정리합니다. 여유가 있으면 now를 추가하고 (True,0), 꽉 찼으면 추가하지 않고 (False,retry_after)를 반환합니다.

6. /root/work/fa-rate-window-lab/service.py에서 client_key(value)는 1~40자 ASCII 영문·숫자·하이픈 문자열을 그대로 반환하고 나머지는 ValueError입니다.

7. /root/work/fa-rate-window-lab/service.py에서 limited_response(wait)는 상태 429, 본문 {error:'rate_limited'}, Retry-After 헤더는 wait를 문자열로 만든 JSONResponse입니다.

8. /root/work/fa-rate-window-lab/service.py에서 create_app(clock, limit=2, window=10)는 GET /work에서 X-Client-ID를 검사해 잘못된 키는 400 {error:'invalid_client'}, 허용은 200 {ok:True}, 초과는 limited_response입니다. state는 앱별로 분리합니다.

참고

8단계

  1. 설정을 검증한다
  2. 창의 왼쪽 경계를 제외한다
  3. 대기 시간을 올림한다
  4. 키마다 기록을 나눈다
  5. 허용 요청만 기록한다
  6. 클라이언트 키를 검증한다
  7. 거절 응답을 만든다
  8. 가상 시간으로 요청 흐름을 끝낸다