Rate Limiter: Token Bucket

rate-limiter
Mechanism lab

Rate Limiter: Token Bucket

A rate limiter that allows every request because it does no accounting. Fix it with a token bucket: consume a token per request, refill over logical ticks, reject when empty.

You'll predict the failure first, then write the fix and watch your own code drive the behavior.

Understand it step by step (한국어로 이해 → 영어로 말하기)

손님이 너무 많이 몰리면 가게가 무너진다. 그래서 "1초에 몇 번까지만 받기"처럼 요청 속도에 제한을 둬야 한다.
  1. 1

    어떤 사람이 우리 서버에 요청을 계속 보낸다. 너무 빠르게, 너무 많이 보낸다.

    🗣 영어로 말해

    A user keeps sending requests to our server, too fast and too many.

    checking microphone…

  2. 2

    지금 코드는 들어오는 요청을 무조건 다 받는다. 세지도 않고, 막지도 않는다.

    ⚖️ Trade-off: 그냥 다 받으면 서버가 과부하로 느려지거나 죽을 수 있다.

    boolean allow(int nowTick) {
        return true;   // BUG: accepts everything, no token accounting
    }
    🗣 영어로 말해

    Right now the code accepts every request without counting or blocking anything.

    checking microphone…

  3. 3

    그래서 '토큰 통'을 만든다. 통에 동전(토큰)이 N개 들어있다고 생각하면 된다. 요청 1번에 동전 1개를 쓴다.

    🔧 도구:token bucketcapacity N

    class RateLimiter {
        int capacity, tokens;
        RateLimiter(int capacity) { this.capacity = capacity; this.tokens = capacity; }
    }
    // each allow() spends one token
    🗣 영어로 말해

    I use a token bucket: it starts with N tokens, and each request spends one token.

    checking microphone…

  4. 4

    통이 비면(동전이 0개) 더 이상 요청을 못 받는다. 그 요청은 거절한다.

    ⚖️ Trade-off: 정상 사용자라도 통이 비어 있으면 거절당할 수 있다.

    ✅ Fix: 그게 바로 우리가 원하는 거다. 한도를 넘은 요청은 막는 게 목적이다.

    boolean allow(int nowTick) {
        if (tokens <= 0) return false;   // bucket empty -> reject
        tokens--;
        return true;
    }
    🗣 영어로 말해

    When the bucket is empty, I reject the request because the limit is reached.

    checking microphone…

  5. 5

    통은 시간이 지나면 다시 채워진다. 1틱(시간 한 칸)마다 동전 1개씩 넣어준다. 단, 통 크기 N을 넘게는 안 넣는다.

    ⚖️ Trade-off: 쉬는 동안 동전을 무한정 모아두면, 한 번에 몰아서 쏠 수 있다.

    ✅ Fix: 그래서 통 크기를 N으로 막아둔다(capped at capacity). 아무리 오래 쉬어도 N개까지만 모인다.

    🔧 도구:refill per tickcap at capacity

    int elapsed = nowTick - lastTick;
    if (elapsed > 0) {
        tokens = Math.min(capacity, tokens + elapsed);   // 1 per tick, capped
        lastTick = nowTick;
    }
    🗣 영어로 말해

    Tokens refill one per tick over time, but never above the capacity.

    checking microphone…

  6. 6

    비용은 거의 없다. 요청마다 계산은 O(1)이고, 저장하는 건 정수 몇 개(토큰 수, 마지막 시각)뿐이다.

    🔧 도구:O(1)int state

    int capacity, tokens, lastTick;   // O(1) per allow(), a few ints of state
    🗣 영어로 말해

    The cost is tiny: O(1) per call and just a few integers of state.

    checking microphone…

  7. 7

    한 가지 한계: 이 통은 서버 한 대 안에서만 센다. 서버가 여러 대면 각자 자기 통을 가져서 전체 한도가 깨진다.

    ⚖️ Trade-off: 서버 3대면 한도가 3배로 풀려버린다. 서버끼리 토큰 수를 모른다.

    ✅ Fix: 여러 대를 진짜로 묶으려면 통을 공유 저장소(Redis)에 둬서 모든 서버가 같은 통을 본다.

    🔧 도구:Redisshared state

    // per-instance only; share the bucket across servers via Redis
    jedis.eval(tokenBucketScript, key, nowTick, capacity);
    🗣 영어로 말해

    The limit is per-instance, so distributed enforcement needs shared state like Redis.

    checking microphone…

7단계 영어를 다 말하면 → 이 메커니즘 전체를 영어로 설명할 수 있게 된다.

Explain it in English

Say it out loud in your own words — hit these terms — then check against the gold answer.

Say it in your own words — but hit these: rate limiter · token bucket · concurrency · java

checking microphone…