限流算法深度解析:从计数器到令牌桶

核心观点: 限流算法不是单纯的技术选择,而是业务需求与技术约束的权衡。本文从一个真实需求的演进过程出发,深度剖析5种限流算法的设计思路、实现细节和适用场景。 引子:一个API接口的限流需求演进 需求背景 你是某电商平台的后端工程师,负责维护商品查询接口 /api/products/{id}。 初始状态(无限流): @RestController public class ProductController { @Autowired private ProductService productService; @GetMapping("/api/products/{id}") public Result getProduct(@PathVariable Long id) { Product product = productService.getById(id); return Result.success(product); } } 系统运行良好,日常流量500 QPS,数据库和服务器完全可以承受。 第一次危机:流量突增 某天,运营部门做了一个促销活动,流量突然涨到5000 QPS(10倍)。 后果: 10:00:00 - 活动开始,流量5000 QPS 10:00:30 - 数据库连接池耗尽(最大连接数100) 10:01:00 - 响应时间从50ms暴增到5秒 10:01:30 - 服务器CPU 100%,系统崩溃 CTO找你谈话: “给这个接口加个限流,最多支持1000 QPS。” 你的第一反应:计数器! 需求演进1:固定窗口计数器 需求: 每秒最多1000个请求。 你快速实现了一个固定窗口计数器: public class FixedWindowRateLimiter { private final int maxRequests; private final AtomicInteger counter = new AtomicInteger(0); private volatile long windowStart = System.currentTimeMillis(); public FixedWindowRateLimiter(int maxRequests) { this.maxRequests = maxRequests; } public boolean tryAcquire() { long now = System.currentTimeMillis(); // 新窗口,重置计数器 if (now - windowStart >= 1000) { synchronized (this) { if (now - windowStart >= 1000) { counter.set(0); windowStart = now; } } } // 当前窗口未超限 return counter.incrementAndGet() <= maxRequests; } } 部署上线,问题解决!系统稳定运行。 ...

2025-11-03 · maneng

限流算法:固定窗口、滑动窗口与令牌桶

限流算法对比 算法 实现难度 精确度 内存占用 适用场景 固定窗口 简单 低 低 粗粒度限流 滑动窗口 中等 高 中 精确限流 漏桶 中等 高 中 流量整形 令牌桶 复杂 高 中 允许突发 1. 固定窗口算法 原理:时间窗口内计数,超过限制则拒绝 @Service public class FixedWindowRateLimiter { @Autowired private RedisTemplate<String, String> redis; // 限流检查 public boolean isAllowed(String key, int limit, int windowSeconds) { String counterKey = key + ":" + (System.currentTimeMillis() / 1000 / windowSeconds); Long current = redis.opsForValue().increment(counterKey); if (current == 1) { redis.expire(counterKey, windowSeconds, TimeUnit.SECONDS); } return current != null && current <= limit; } } // 使用示例 public boolean checkLimit(String userId) { return rateLimiter.isAllowed("api:user:" + userId, 100, 60); // 每分钟100次 } 问题:临界问题 ...

2025-01-21 · maneng

限流算法原理:计数器、滑动窗口、令牌桶、漏桶

引言:算法决定限流的精度 前面我们学会了使用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; // 限流 } } } 使用示例: ...

2025-01-21 · maneng

如约数科科技工作室

浙ICP备2025203501号

👀 本站总访问量 ...| 👤 访客数 ...| 📅 今日访问 ...