Foundry
처리율 제한 장치 설계
심화
핵심

토큰 버킷과 버스트 정책

순간 허용량을 정책으로 정한다

여기까지는 얼마나 정확히 세는가의 이야기였습니다. 토큰 버킷은 다른 질문에 답합니다. 순간적으로 몰아 쓰는 것을 얼마나 허용할지입니다.

세는 것이 아니라 나눠 주는 것

토큰이 일정 속도로 차고 쓰지 않으면 통 크기까지 쌓여 그만큼의 버스트가 허용된다 초당 17개씩 찬다. 통은 1,000개까지 지금 500개 쓰지 않으면 쌓인다 통이 차면 더 쌓이지 않는다 그래서 최대 버스트가 통 크기다 요청 하나가 토큰 하나를 가져간다. 없으면 거절한다 평균 속도와 순간 허용량을 따로 정하는 방식이다

한도 분당 1,000회 을 초당 약 17개로 바꿔 토큰을 채웁니다. 요청 하나가 토큰 하나를 가져가고, 없으면 거절합니다. 쓰지 않은 토큰은 쌓이지만 통 크기까지만 쌓입니다.

여기서 두 값이 분리됩니다.

정하는 것
채우는 속도오래 보면 평균 몇 회까지 쓸 수 있는지
통 크기한 번에 몰아 쓸 수 있는 최대량

앞의 방식들은 이 두 값이 한 숫자에 묶여 있었습니다. 분당 1,000회라고 하면 1,000회를 1초에 다 쓰는 것도 허용됩니다. 토큰 버킷은 평균은 유지하면서 순간 허용량만 따로 줄일 수 있습니다.

버스트를 허용하는 것이 오히려 필요하다

몰아 쓰기를 전부 막으면 정상 사용자가 불편해집니다. 목록을 불러온 뒤 상세를 20건 조회하는 화면은 짧은 순간에 21회를 씁니다.

통 크기 1,000: 하루 조용하던 키가 갑자기 1,000회를 쓸 수 있다
통 크기 50: 순간 몰아 쓰기를 50회로 제한한다. 평균은 그대로

통 크기가 곧 정책입니다. 서버 용량이 걱정이면 줄이고, 사용자 편의가 중요하면 늘립니다. 이 값을 요구사항에서 정하지 않으면 대개 한도와 같게 두는데, 그러면 앞의 방식들과 다를 바가 없습니다.

토큰을 실제로 채우지 않는다

초당 17번 값을 올리는 작업을 키마다 돌리면 활성 키 10만 개에 초당 170만 번의 쓰기가 됩니다. 그럴 필요가 없습니다.

마지막으로 만진 시각과 그때 남은 토큰 수만 저장한다
요청이 올 때 지난 시간을 재서 그동안 찼을 만큼을 더한다
통 크기를 넘지 않게 자른다

요청이 없는 동안에는 아무 일도 하지 않습니다. 시각과 개수 두 값만 있으면 언제든 지금 상태를 계산할 수 있습니다. 조용한 키에 비용이 들지 않는 것이 이 방식의 큰 장점입니다.

두 값을 함께 고쳐야 한다

시각과 토큰 수를 따로 읽고 쓰면 동시 요청이 서로를 덮습니다. 저장소가 여러 값을 한 번에 다뤄 주는 수단(원자적 실행이나 조건부 갱신)이 필요합니다. 다음 단계에서 이 문제를 다룹니다.

비용이 가장 작은 방식이다

방식키마다 저장
시각 전부통과 요청 수만큼
1초 버킷 60개정수 60개
토큰 버킷시각 하나와 개수 하나

정확도 이야기와 섞이지 않게 유의합니다. 토큰 버킷은 평균을 정확히 지키면서 순간 허용량을 따로 정하는 방식이고, 슬라이딩 윈도우 계열은 최근 구간의 개수를 정확히 세는 방식입니다. 무엇을 지켜야 하는지에 따라 고릅니다.

면접에서 이렇게 나옵니다

Q.토큰 버킷 방식을 설명해 주세요

한도를 채우는 속도로 바꿔 토큰을 나눠 주고, 요청이 토큰을 가져가게 합니다.

분당 1,000회 이면 초당 약 17개를 채웁니다. 쓰지 않은 토큰은 쌓이지만 통 크기까지만 쌓입니다.

정하는 것
채우는 속도오래 보면 평균 몇 회까지 쓸 수 있는지
통 크기한 번에 몰아 쓸 수 있는 최대량

앞의 방식들은 이 두 값이 한 숫자에 묶여 있습니다. 토큰 버킷은 평균은 유지하면서 순간 허용량만 따로 줄일 수 있습니다.

흔한 실수: 통 크기를 한도와 같게 두고 넘어가는 것. 그러면 조용하던 키가 갑자기 1,000회를 몰아 쓸 수 있어 앞의 방식들과 다를 바가 없어집니다. 통 크기는 따로 정해야 의미가 있는 값입니다.

Q.토큰을 초당 17번씩 채우면 쓰기가 너무 많지 않나요

실제로 채우지 않습니다. 마지막으로 만진 시각과 그때 남은 개수만 저장하고, 요청이 올 때 계산합니다.

지난 시간 x 채우는 속도 = 그동안 찼을 양
남은 토큰 + 찼을 양, 통 크기를 넘지 않게 자른다

요청이 없는 동안에는 아무 일도 하지 않습니다. 활성 키가 10만 개여도 조용한 키에는 비용이 들지 않습니다.

주기적으로 값을 올리는 방식이면 키 수와 속도를 곱한 만큼의 쓰기가 상시로 발생합니다. 초당 17번에 키 10만 개면 초당 170만 번입니다.

흔한 실수: 시각과 토큰 수를 따로 읽고 쓰는 것. 동시 요청이 서로를 덮어 토큰이 두 번 쓰이거나 되살아납니다. 두 값은 함께 고쳐야 하므로 저장소의 원자적 실행이나 조건부 갱신이 필요합니다.

Q.버스트를 얼마나 허용해야 하나요

통 크기로 정하고, 그 값은 서버 용량과 사용자 편의의 균형점입니다.

몰아 쓰기를 전부 막으면 정상 사용자가 불편해집니다. 목록을 불러온 뒤 상세를 20건 조회하는 화면은 짧은 순간에 21회를 씁니다.

통 크기 1,000: 조용하던 키가 갑자기 1,000회를 쓴다
통 크기 50: 순간 몰아 쓰기를 50회로 제한한다. 평균은 그대로

정할 근거는 서버가 순간적으로 견디는 양입니다. 그래서 이 값은 사용자와의 약속이 아니라 우리 용량에서 나옵니다.

흔한 실수: 버스트 허용을 나쁜 것으로 보고 통 크기를 1에 가깝게 두는 것. 그러면 요청을 정확히 일정한 간격으로 보내야 하는데, 실제 클라이언트는 그렇게 동작하지 않습니다. 평균을 지키는 것과 순간을 고르게 만드는 것은 다른 요구입니다.

Q.토큰 버킷과 슬라이딩 윈도우 중 무엇을 고르시겠습니까

무엇을 지켜야 하는지에 따라 갈립니다. 정확도 비교가 아닙니다.

방식지키는 것
토큰 버킷평균 속도와 순간 허용량을 따로
슬라이딩 윈도우최근 구간의 개수

"최근 1분에 1,000회" 를 문자 그대로 지켜야 한다면 슬라이딩 윈도우입니다. 사용자에게 알린 규칙이 그것이기 때문입니다.

서버 보호가 목적이고 순간 부하를 통제하고 싶다면 토큰 버킷이 낫습니다. 저장 비용도 가장 작습니다.

흔한 실수: 둘을 정확도 순으로 줄 세우는 것. 토큰 버킷도 평균은 정확히 지킵니다. 다른 것은 무엇을 세는지이고, 그래서 사용자에게 알린 규칙의 문장이 선택을 정합니다.

먼저 스스로 답해보고 아래 답변과 견줘보세요. 막히는 부분은 문제로 확인할 수 있어요.

읽었으면 문제로 확인해보세요

처리율 제한 장치 설계 문제를 풀면 틀린 문제가 자동으로 노트에 쌓입니다. 가입 없이 5문제를 먼저 풀어볼 수도 있어요.