본문으로 바로가기

31

레이트 리미팅

전부 받으려다 전부 잃는 대신, 감당할 만큼만 받고 나머지는 정직하게 거절한다

먼저 보면 좋은 강의: 지연 시간과 처리량

단계별로 보기

스페이스바로 재생/정지, 좌우 화살표로 단계 이동, R로 처음으로 돌아갑니다.

대기 중1 / 4 단계 — 정원 없이 다 태운다

리미터가 없습니다. 초당 120건이 그대로 서버로 들어갑니다. 서버의 한계는 초당 50건입니다. 대기열 스무 자리가 순식간에 차고 포화도가 100퍼센트가 됩니다. 서버는 지금 죽기 직전입니다.

유입 요청초당 120건

이 강의를 마치면

  • 거절이 실패가 아니라 의도된 방어라는 점을 설명한다.
  • 제한 없이 다 받았을 때와 제한을 두었을 때, 서버 상태가 어떻게 달라지는지 구분한다.
  • 제한선을 어디에 그을지 정하는 기준(뒤쪽의 처리 한계)을 설명한다.
  • 약한 서버 앞에 리미터를 세워 서버를 포화에서 구해낸다.

다 받으면 다 잃는다

초당 50건이 한계인 서버에 초당 120건이 들어옵니다. 서버는 착하게 전부 받으려고 합니다. 결과가 어떻게 될까요?

앞선 강의에서 봤습니다. 대기열이 쌓이고, 응답이 느려지고, 대기열마저 차면 버립니다. 나쁜 것은 버려지는 요청이 아무 규칙 없이 정해진다는 점입니다. 줄을 서서 한참 기다리다가 결국 오류를 받는 사람이 생깁니다. 기다리게 하고 나서 실패시킨 것입니다. 가장 나쁜 조합입니다.

더 심하면 서버가 아예 죽습니다. 그러면 초당 50건마저 처리하지 못합니다. 전부 받으려다 전부 잃었습니다.

엘리베이터의 정원

엘리베이터에 정원 15명이 적혀 있습니다. 20명이 타려 하면 경고음이 울리고 문이 안 닫힙니다. 불친절해 보이지만, 이 규칙이 없으면 아무도 못 올라갑니다.

15명에서 끊으면 어떻게 될까요.

  • 15명은 확실히 올라갑니다.
  • 못 탄 5명은 즉시 압니다. 계단으로 가든 다음 차를 기다리든 선택할 수 있습니다.

여기서 중요한 것은 5명이 못 탔다는 사실이 아니라, 그들이 즉시 알았다는 점입니다. 20분간 갇혀 있다가 "못 갑니다"를 듣는 것과는 완전히 다릅니다.

리미터가 하는 일

레이트 리미터는 서버 앞에 서서 정원을 셉니다. 한도를 넘으면 즉시 거절합니다.

들어옴 120/s ──> [리미터: 40/s만 통과] ──> 서버(한계 50/s)
                      │
                      └──> 80/s는 즉시 거절 (429 Too Many Requests)

거절은 즉시입니다. 이게 핵심입니다. 기다리게 하지 않습니다. 거절당한 쪽은 곧바로 알고 다음 행동을 정합니다.

서버 쪽에서 본 풍경

리미터 없음리미터 있음
서버 대기열꽉 참 (20/20)비어 있음 (0)
서버 포화도100%0%
통과한 요청의 운명느리고, 그마저 버려짐빠르게 처리됨
서버의 안위죽을 수 있음멀쩡함

서버가 한가해집니다. 리미터가 애초에 감당할 만큼만 들여보내기 때문입니다. 통과한 요청은 줄을 서지 않고 곧바로 처리됩니다.

여기서 정직해야 할 부분

많은 사람이 오해합니다. "리미터를 붙이면 오류가 사라진다"고요. 아닙니다.

초당 120건이 들어오는데 40건만 통과시키면 80건은 거절됩니다. 거절도 사용자 입장에서는 실패입니다. 전체 오류율은 여전히 높습니다. 리미터는 일감을 늘려주지 않습니다.

그럼 뭐가 좋아진 걸까요? 실패의 성질이 바뀌었습니다.

리미터 없음리미터 있음
실패를 언제 아나한참 기다린 뒤즉시
누가 실패하나운에 따라 아무나한도를 넘은 쪽
성공한 요청의 품질느림빠름
서버가 죽을 위험있음없음

리미터는 용량을 늘리는 도구가 아니라 용량을 지키는 도구입니다. 진짜로 120건을 다 처리하고 싶으면 서버를 늘려야 합니다. 리미터는 그 서버를 늘릴 때까지 시스템이 살아 있게 해줍니다.

선을 어디에 긋는가

제한선은 취향이 아니라 뒤쪽의 처리 한계에서 나옵니다.

  • 서버가 초당 50건을 처리한다면 → 리미터는 50건 근처에 둡니다.
  • 너무 낮게 잡으면(10건) 서버가 놀고 있는데 손님을 돌려보냅니다. 손해입니다.
  • 너무 높게 잡으면(200건) 리미터가 있으나 마나입니다. 서버가 다시 무너집니다.

그래서 리미터를 붙이기 전에 반드시 해야 할 일이 있습니다. 뒤쪽이 실제로 얼마나 처리하는지 재는 것입니다. 이 숫자를 모르면 제한선은 그냥 추측입니다.

잠깐 몰리는 것은 봐준다

현실의 트래픽은 고르지 않습니다. 평균은 초당 40건인데 어떤 순간엔 60건이 몰립니다. 칼같이 40건에서 끊으면 멀쩡한 요청이 억울하게 거절됩니다.

그래서 흔히 토큰 버킷을 씁니다. 한가할 때 토큰을 모아뒀다가 잠깐 몰릴 때 그것으로 받아줍니다. 평균은 지키되 순간의 출렁임은 흡수하는 것입니다.

직접 해보세요

아래 과제의 서버는 초당 50건이 한계인데 120건을 맞고 있습니다. 먼저 실행해서 서버 대기열이 꽉 차고 포화도가 100%인 것을 확인하세요. 그다음 리미터를 세워 서버를 구해내세요. 전체 오류율은 여전히 높을 것입니다. 그것이 정상입니다. 우리가 구하려는 것은 서버입니다.

이 강의가 단순화한 것

교육용 모형은 언제나 무언가를 생략합니다. 무엇을 생략했는지 아는 것도 학습의 일부입니다.

전제한 것

  • 이 과제에서 거절된 요청은 시스템 전체 오류율에 실패로 집계된다. 그래서 리미터를 세워도 전체 오류율은 내려가지 않는다. 이것은 버그가 아니라 정직한 회계이며, 이 강의의 요점 중 하나다.
  • 리미터 자체는 처리 시간이 들지 않는다고 본다. 실제로는 리미터도 자원을 쓰며, 분산 환경에서는 카운터를 공유하는 비용이 상당하다.
  • 이 과제의 제한은 전체 트래픽에 하나로 적용된다. 실제로는 사용자별·API 키별로 따로 세는 경우가 많다.
  • 각 요청의 처리 시간은 일정하다고 본다. 실제로는 요청마다 비용이 달라 '건수'로만 제한하면 무거운 요청이 새어 들어온다.

다루지 않은 것

  • 사용자별·테넌트별 제한과 공정성(한 사용자가 전체 한도를 먹는 문제)
  • 분산 환경에서 여러 리미터가 카운터를 공유하는 방법
  • 429 응답의 Retry-After 헤더와 클라이언트의 협조
  • 고정 윈도우 경계에서 두 배가 통과하는 문제

더 읽을거리: RFC 6585 — Additional HTTP Status Codes (429 Too Many Requests)

이제 직접 만들어 보세요

읽어서 아는 것과 만들어서 아는 것은 다릅니다. 컴포넌트를 배치하고 트래픽을 흘려서 실제로 동작하는지 확인해 보세요.

실습 과제 열기

이 강의에 물어보기

하루 5번

이 강의 본문만 근거로 답합니다. 강의에 없는 내용은 지어내지 않습니다.