限流算法原理:计数器、滑动窗口、令牌桶、漏桶
引言:算法决定限流的精度 前面我们学会了使用Sentinel进行限流,只需要一行配置: rule.setCount(100); // 每秒最多100次 但你有没有想过:Sentinel是如何精确控制每秒100次的? 如果100个请求在1秒的前0.01秒就全部到达,后面0.99秒怎么办? 如果1秒内有120个请求,是拒绝哪20个?前面的?后面的? 如果平时QPS是50,突然来了200的瞬间峰值,能否允许? 这些问题的答案,都隐藏在限流算法中。今天我们将深入理解4种经典限流算法,看看它们各自的特点和适用场景。 一、固定窗口计数器:最简单的限流算法 1.1 算法原理 固定窗口计数器是最直观的限流算法: 时间窗口:每秒钟 限流阈值:100次 秒钟: 0 1 2 3 4 │─────│─────│─────│─────│─────│ 计数: 0→100 0→50 0→120 0→80 0→100 结果: 全通过 全通过 拒20 全通过 全通过 工作流程: 将时间划分为固定窗口(如1秒) 每个窗口维护一个计数器 请求到达时,计数器+1 如果计数器超过阈值,拒绝请求 窗口结束时,计数器归零 1.2 代码实现 public class FixedWindowRateLimiter { private final int limit; // 限流阈值 private final long windowSize; // 窗口大小(毫秒) private AtomicInteger counter; // 当前窗口计数 private long windowStartTime; // 窗口开始时间 public FixedWindowRateLimiter(int limit, long windowSize) { this.limit = limit; this.windowSize = windowSize; this.counter = new AtomicInteger(0); this.windowStartTime = System.currentTimeMillis(); } public synchronized boolean tryAcquire() { long now = System.currentTimeMillis(); // 判断是否需要重置窗口 if (now - windowStartTime >= windowSize) { // 新窗口开始 counter.set(0); windowStartTime = now; } // 判断是否超过限流阈值 if (counter.get() < limit) { counter.incrementAndGet(); return true; // 通过 } else { return false; // 限流 } } } 使用示例: ...