四大常见的限流算法: 限流的目的:通过对并发访问/请求进行限速,或者对一个时间窗口内的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务、排队或等待、降级等处理。 简单理解突增流量超过系统承受的最大流量,会造成系统瘫痪。对于该问题,使用限流器进行限制。
1. 计数器:
- 基本原理:即限制一段时间请求总数。假设A接口,1分钟的访问次数不能超过100个。
- 具体实现:首先,初次调用A接口时记录初始时间startTime,并将计数器counter+1;接着后续的每一个请求来了,判断是否超过限额,如果没超过则counter+1,否则就拒绝访问。此使,当时间超过1分钟,则需要重置时间。
- 优缺点:实现简单。但是无法应对突发流量。即假设59秒时来了100个请求,60秒时来了100个请求。这时系统可能会被瞬间击垮。
- 代码实现:
public class CounterLimit { public static int counter = 0; //计数器 public static long startTime = System.currentTimeMillis(); // 系统启动时间 public static long limit = 100; // 限流大小 public static final long interval = 1000 * 60; // 时间窗口ms public static boolean acquireLimit() { long now = System.currentTimeMillis(); if (now - startTime > interval) { startTime = now; counter = 1; return true; } else { counter++; return counter <= limit; } } }
2. 滑动窗口
- 基本概念:对限流时间进行拆分,为了解决计数器法统计精度太低的问题,引入了滑动窗口算法。
- 具体实现:恒定限流时间大小m,将时间拆分成n个块,每次过了m/n个时间,则丢弃最开始的时间,并添加最后的时间。通过判断最后时间的计数与最开始时间的计数差判断是否达到限流。
- 优缺点:处理精度高,但是开辟更多空间。
- 代码实现:
public class SlidingTimeWindow { public static final int limit = 10;// 时间窗口内最大请求数 public static final long interval = 1000; // 时间窗口ms public static long counter = 0L;// 服务访问次数 public static LinkedList<Long> slots = new LinkedList<>(); // 记录的格子 public static long startTime = System.currentTimeMillis(); // 第一个窗口的开始时间 public static synchronized boolean acquireLimit() { counter++; long now = System.currentTimeMillis(); if (now - startTime > interval || slots.size() == 0) { slots.add(counter); } else { slots.set(slots.size() - 1, counter); } if (slots.size() > 10) { slots.removeFirst(); startTime = startTime+interval; } if (slots.size() == 1) { return slots.getLast() > limit; } else { long count = slots.getLast() - slots.getFirst(); return count > limit; } } public static void main(String[] args) throws InterruptedException { ExecutorService threadPool = Executors.newFixedThreadPool(20); while (true) { threadPool.execute(() -> { boolean b = SlidingTimeWindow.acquireLimit(); System.out.println("线程:" + Thread.currentThread().getName() + "是否限流" + b); }); Thread.sleep(1000); } } }
3. 令牌桶
- 基本概念:用于对网络流量进行整形和速率限制。
- 具体实现:首先固定令牌桶的大小为m,并且系统以恒定速度v向令牌桶中加入令牌,桶中令牌总数最大为m,其它令牌满则溢。当请求到了会消耗一个令牌,只有拿到令牌的请求才会进行处理。
- 代码实现:使用Guava限流工具类RateLimiter实现。
- 优缺点:实现简单,且允许某些流量的突发。
4. 漏桶
- 基本概念:可以粗略的认为注水漏水过程,往桶中以一定速率流出水,以任意速率流入水,当水超过桶流量则丢弃,因为桶容量是不变的,保证了整体的速率。