← 返回首页
🔍

限流

📂 architecture ⏱ 2 min 345 words

限流

限流策略概览

限流通过控制请求速率保护系统,防止突发流量导致服务崩溃,常见算法包括令牌桶、漏桶和滑动窗口。

┌─────────────────────────────────────────┐
│              限流策略                     │
├─────────┬─────────┬─────────┬───────────┤
│ 令牌桶  │  漏桶   │ 滑动窗口 │  固定窗口 │
├─────────┼─────────┼─────────┼───────────┤
│ 允许突发│ 匀速输出│ 高精度  │ 实现简单  │
└─────────┴─────────┴─────────┴───────────┘

令牌桶算法

令牌桶以固定速率生成令牌,请求需获取令牌才能处理,允许一定突发流量。

public class TokenBucket {
    private final int capacity;
    private final double refillRate;
    private double tokens;
    private long lastRefillTime;

    public TokenBucket(int capacity, double refillRate) {
        this.capacity = capacity;
        this.refillRate = refillRate;
        this.tokens = capacity;
        this.lastRefillTime = System.nanoTime();
    }

    public synchronized boolean tryAcquire() {
        refill();
        if (tokens >= 1) {
            tokens -= 1;
            return true;
        }
        return false;
    }

    private void refill() {
        long now = System.nanoTime();
        double elapsed = (now - lastRefillTime) / 1_000_000_000.0;
        tokens = Math.min(capacity, tokens + elapsed * refillRate);
        lastRefillTime = now;
    }
}

漏桶算法

漏桶以恒定速率处理请求,平滑流量峰值,适合对输出速率有严格要求的场景。

public class LeakyBucket {
    private final int capacity;
    private final long leakInterval;
    private int water;
    private long lastLeakTime;

    public LeakyBucket(int capacity, long leakInterval) {
        this.capacity = capacity;
        this.leakInterval = leakInterval;
        this.lastLeakTime = System.currentTimeMillis();
    }

    public synchronized boolean tryAcquire() {
        leak();
        if (water < capacity) {
            water++;
            return true;
        }
        return false;
    }

    private void leak() {
        long now = System.currentTimeMillis();
        long elapsed = now - lastLeakTime;
        int leaked = (int) (elapsed / leakInterval);
        if (leaked > 0) {
            water = Math.max(0, water - leaked);
            lastLeakTime = now;
        }
    }
}

滑动窗口限流

滑动窗口统计时间窗口内的请求数,精度高于固定窗口,避免边界突发问题。

public class SlidingWindowRateLimiter {
    private final int maxRequests;
    private final long windowMs;
    private final LinkedList<Long> timestamps = new LinkedList<>();

    public SlidingWindowRateLimiter(int maxRequests, long windowMs) {
        this.maxRequests = maxRequests;
        this.windowMs = windowMs;
    }

    public synchronized boolean tryAcquire() {
        long now = System.currentTimeMillis();
        while (!timestamps.isEmpty() && timestamps.peekFirst() <= now - windowMs) {
            timestamps.pollFirst();
        }
        if (timestamps.size() < maxRequests) {
            timestamps.addLast(now);
            return true;
        }
        return false;
    }
}

分布式限流

// Redis Lua脚本实现分布式令牌桶
String script = """
    local key = KEYS[1]
    local capacity = tonumber(ARGV[1])
    local rate = tonumber(ARGV[2])
    local now = tonumber(ARGV[3])
    local tokens = tonumber(redis.call('GET', key) or capacity)
    local lastTime = tonumber(redis.call('HGET', key, 'time') or now)
    local elapsed = now - lastTime
    tokens = math.min(capacity, tokens + elapsed * rate)
    if tokens >= 1 then
        tokens = tokens - 1
        redis.call('SET', key, tokens)
        redis.call('HSET', key, 'time', now)
        return 1
    end
    return 0
    """;