常见限流算法及实现

embedded/2025/3/19 14:45:26/

1. 固定窗口计数器(Fixed Window Counter)

  • 原理:在固定时间窗口(如1分钟)内统计请求数,超过阈值则拒绝后续请求。
  • 优点:实现简单,内存占用低。
  • 缺点:存在窗口切换时的流量突增问题(如相邻窗口边界处可能允许双倍流量)。
/*** @description: 固定窗口限流* @Author: whopxx* @CreateTime: 2025-03-16*/
public class FixedWindowLimiter {private final int limit;      // 窗口内最大请求数private final long windowMs;  // 窗口时间(毫秒)private final AtomicInteger counter = new AtomicInteger(0);private volatile long windowStart = System.currentTimeMillis();public FixedWindowLimiter(int limit, long windowMs) {this.limit = limit;this.windowMs = windowMs;}public boolean tryAcquire() {long now = System.currentTimeMillis();if (now - windowStart > windowMs) {synchronized (this) {if (now - windowStart > windowMs) {windowStart = now;counter.set(0);}}}return counter.incrementAndGet() <= limit;}public static void main(String[] args) {FixedWindowLimiter fixedWindowLimiter = new FixedWindowLimiter(5, 1000);for (int i = 0; i < 100; i++) {boolean b = fixedWindowLimiter.tryAcquire();System.out.println(b);try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

2. 滑动窗口计数器(Sliding Window Counter)

  • 原理:将时间分割为更细粒度的子窗口(如每分钟分为60个1秒窗口),统计最近完整时间窗口内的请求总数。
  • 优点:缓解固定窗口的临界问题,限流更平滑。
  • 缺点:实现较复杂,需存储子窗口的请求记录。
/*** @description: 滑动窗口限流* @Author: whopxx* @CreateTime: 2025-03-16*/
public class SlidingWindowLimiter {private final long windowMs;          // 窗口总时间(毫秒)private final int subWindowCount;     // 子窗口数量private final long subWindowMs;       // 子窗口时间(毫秒)private final int limit;              // 窗口内最大请求数private final AtomicInteger[] counters;private final long[] windowStartTimes; // 每个子窗口的起始时间private final ReentrantLock lock = new ReentrantLock();public SlidingWindowLimiter(int limit, long windowMs, int subWindowCount) {this.limit = limit;this.windowMs = windowMs;this.subWindowCount = subWindowCount;this.subWindowMs = windowMs / subWindowCount;this.counters = new AtomicInteger[subWindowCount];this.windowStartTimes = new long[subWindowCount];for (int i = 0; i < subWindowCount; i++) {counters[i] = new AtomicInteger(0);windowStartTimes[i] = System.currentTimeMillis() - i * subWindowMs;}}public boolean tryAcquire() {long now = System.currentTimeMillis();lock.lock();try {// 1. 清理所有过期的子窗口int expiredCount = 0;for (int i = 0; i < subWindowCount; i++) {if (now - windowStartTimes[i] > windowMs) {expiredCount += counters[i].getAndSet(0);windowStartTimes[i] = now - (now % subWindowMs); // 对齐时间窗口}}// 2. 计算当前子窗口索引int currentSubIdx = (int) ((now % windowMs) / subWindowMs);// 3. 统计当前窗口内总请求数int total = expiredCount;for (int i = 0; i < subWindowCount; i++) {total += counters[i].get();}if (total >= limit) {return false;}// 4. 写入当前子窗口counters[currentSubIdx].incrementAndGet();return true;} finally {lock.unlock();}}public static void main(String[] args) throws InterruptedException {// 测试:限制1秒内最多2次请求,窗口分为5个子窗口(每个200ms)SlidingWindowLimiter limiter = new SlidingWindowLimiter(2, 1000, 5);for (int i = 0; i < 10; i++) {System.out.println("Request " + (i + 1) + ": " + (limiter.tryAcquire() ? "OK" : "Limited"));Thread.sleep(150); // 模拟请求间隔150ms}}
}

3. 漏桶算法(Leaky Bucket)

  • 原理:请求像水一样进入桶中,桶以固定速率“漏水”(处理请求),桶满则拒绝新请求。
  • 优点:强制恒定速率处理,适合平滑突发流量。
  • 缺点:无法灵活应对突发流量(即使系统空闲时也无法瞬时处理大量请求)。
/*** @description: 漏桶限流器* @Author: whopxx* @CreateTime: 2025-03-16*/
public class LeakyBucketLimiter {private final long capacity;     // 桶容量private final long rate;         // 流出速率,每秒rate个private AtomicLong water = new AtomicLong(0);          // 当前水量private long lastLeakTime = System.currentTimeMillis();public LeakyBucketLimiter(long capacity, long rateMsPerReq) {this.capacity = capacity;this.rate = rateMsPerReq;}public synchronized boolean tryAcquire() {if (water.get() == 0){lastLeakTime = System.currentTimeMillis();water.set(1);return true;}water.set(water.get() - (System.currentTimeMillis() - lastLeakTime) / 1000 * rate);water.set(water.get() < 0 ? 0 : water.get());lastLeakTime += (System.currentTimeMillis() - lastLeakTime) / 1000 * 1000;if (water.get() >= capacity) {return false;} else {water.set(water.get() + 1);return true;}}public static void main(String[] args) {LeakyBucketLimiter limiter = new LeakyBucketLimiter(5, 1); // 容量为5,流出速率为1个/秒for (int i = 0; i < 100; i++) {boolean b = limiter.tryAcquire();System.out.println(b);try {Thread.sleep(200);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

4. 令牌桶算法(Token Bucket)

  • 原理:系统以固定速率生成令牌存入桶,请求需获取令牌才能处理,无令牌时触发限流。
  • 优点:允许突发流量(桶内积累的令牌可一次性使用),更灵活。
  • 缺点:需维护令牌生成逻辑,实现较复杂。
  • 典型应用:Guava的RateLimiter、Nginx限流模块。
/*** @description: 令牌桶限流算法* @Author: whopxx* @CreateTime: 2025-03-16*/
public class TokenBucketLimiter {//桶的容量private final long capacity;//放入令牌的速率 每秒放入的个数private final long rate;//上次放置令牌的时间private static long lastTime = System.currentTimeMillis();//桶中令牌的余量private static AtomicLong tokenNum = new AtomicLong();public TokenBucketLimiter(int capacity, int permitsPerSecond) {this.capacity = capacity;this.rate = permitsPerSecond;tokenNum.set(capacity);}public synchronized  boolean tryAcquire() {//更新桶中剩余令牌的数量long now = System.currentTimeMillis();tokenNum.addAndGet((now - lastTime) / 1000 * rate);tokenNum.set(Math.min(capacity, tokenNum.get()));//更新时间lastTime += (now - lastTime) / 1000 * 1000;//桶中还有令牌就放行if (tokenNum.get() > 0) {tokenNum.decrementAndGet();return true;} else {return false;}}//测试public static void main(String[] args) {TokenBucketLimiter limiter = new TokenBucketLimiter(3, 2); // 允许每秒放入2个令牌,桶容量为5for (int i = 0; i < 100; i++) {if (limiter.tryAcquire()) {System.out.println("成功请求");} else {System.out.println("请求失败");}try {Thread.sleep(300); // 模拟请求间隔} catch (InterruptedException e) {e.printStackTrace();}}}
}


对比总结

方式

适用场景

固定窗口

简单低频场景

滑动窗口

需平滑限流的场景

漏桶算法

恒定速率处理(如流量整形)

令牌桶算法

允许突发的场景(如秒杀)


http://www.ppmy.cn/embedded/173880.html

相关文章

鸿蒙跳转到系统设置app界面

// 跳转系统app设置界面static startToSystemSetting(){let context getContext() as common.UIAbilityContext;let want: Want {bundleName: com.huawei.hmos.settings,//设置应用bundleNameabilityName: com.huawei.hmos.settings.MainAbility,//设置应用abilityNameuri:…

基于Springboot+服务器磁盘的本地文件存储方案

[local-file-system]基于服务器磁盘的本地文件存储方案 仅提供后端方案 github 环境 JDK11linux/windows/mac 应用场景 适用于ToB业务&#xff0c;中小企业的单体服务&#xff0c;仅使用磁盘存储文件的解决方案 仅使用服务器磁盘存储 与业务实体相结合的文件存储方案&…

华为ISC+战略规划项目数字化转型驱动的智慧供应链革新(169页PPT)(文末有下载方式)

资料解读&#xff1a;华为ISC战略规划项目数字化转型驱动的智慧供应链革新 详细资料请看本解读文章的最后内容。 华为的ISC战略规划项目是其供应链数字化转型的核心&#xff0c;旨在通过智慧供应链的革新&#xff0c;提升企业的竞争力和运营效率。本文将从多个维度详细解读这…

TCP 通信流程图

下面给出一个详细的 TCP 通信流程图&#xff0c;演示 客户端&#xff08;Client&#xff09; 与 服务器&#xff08;Server&#xff09; 之间通过 TCP 协议进行通信时的各个步骤。这里假设&#xff1a; 服务器 IP&#xff1a;192.168.1.100&#xff0c;监听 80 端口客户端 IP&…

ora-600 ktugct: corruption detected---惜分飞

接手一个oracle 21c的库恢复请求,通过Oracle数据库异常恢复检查脚本(Oracle Database Recovery Check)脚本检测之后,发现undo文件offline之后,做了resetlogs操作,导致该文件目前处于WRONG RESETLOGS状态 尝试恢复数据库ORA-16433错误 SQL> recover datafile 1; ORA-00283:…

Spring Bean 生命周期深度解析:原理、场景与优化策略

一、生命周期核心阶段与技术原理 1. 实例化阶段&#xff1a;反射与缓存机制 Spring 通过反射创建 Bean 实例&#xff0c;单例 Bean 在容器启动时初始化&#xff0c;原型 Bean 在首次获取时创建。为解决循环依赖问题&#xff0c;Spring 采用三级缓存机制&#xff1a; 一级缓存…

《声音的未来:语音识别文献解读》专栏介绍及其文章解读目录

声音的未来&#xff1a;语音识别文献解读 ——探索语音技术的前沿&#xff0c;解读未来的声音世界—— 专栏介绍 欢迎来到 “声音的未来&#xff1a;语音识别文献解读”&#xff01;这是一个专注于语音识别领域前沿研究与技术突破的深度解读专栏。在这里&#xff0c;我们将带…

OneCyber 平台

OneCyber 平台是一个专注于 网络安全 和 风险管理 的综合性解决方案平台。它旨在帮助企业和组织应对日益复杂的网络威胁&#xff0c;提供从威胁检测、风险评估到响应和恢复的全方位服务。以下是关于 OneCyber 平台的一些关键信息&#xff1a; 核心功能 威胁检测与分析&#xff…