【设计】限流算法

1,304 阅读2分钟

这是我参与更文挑战的第16天,活动详情查看:更文挑战

一、前言

服务限流:服务限流是限制请求的数量,即某个时间窗口内的请求速率。

二、限流算法

限流算法有:

  1. 计数器
  2. 滑动窗口计数
  3. 漏斗
  4. 令牌桶

(1)计数器

计数器:是使用计数器在周期内累加访问次数,当达到设定的阈值时,触发限流策略。下一个周期开始时,进行清零,重新计数。比如1分钟内限制请求总数为 100。如果超过100 则返回失败。

计数器的缺陷:

临界问题。 比如1分钟内限制请求总数为100的场景下,前一个一分钟内直到这一分钟快结束的时候才来了 100 个请求,而后一个一分钟刚开始就立即来了100个请求。 虽然是这两个不同的一分钟区间,但是事实上不到一分钟的时间内,来了200个请求。因此计数器限流失败。

如图:

2021-01-2120-53-01.png

简单实现如下:

class Counter {

    public long timeStamp = System.currentTimeMillis();
    public AtomicInteger reqCount = new AtomicInteger(0);
    // 时间窗口内最大请求数
    public final int limit = 5;
    // 时间窗口ms 60s, 1分钟
    public final long interval = 60 * 1000;
    public boolean acquire() {
        
        long now = System.currentTimeMillis();
        if (now < timeStamp + interval) {
            // 在时间窗口内
            int cnt = reqCount.getAndIncrement();
            // 判断当前时间窗口内是否超过最大请求控制数
            return cnt <= limit;
        } else {
            // 重置
            timeStamp = now;
            // 超时后重置
            reqCount.set(1);
            return true;
        }
    }
}

(2)滑动窗口计数

滑动窗口计数:将时间周期进一步划分为 N 个小周期,分别记录每个小周期内访问次数,并且根据时间滑动删除过期的小周期。

如图:

2021-01-2120-53-40.png

滑动窗口的单位区间划分越多,滑动窗口的滚动就越平滑,限流统计就会越精确。

(3)漏斗

漏斗:是控制数据注入到网络的速率,平滑网络上的突发流量。漏桶算法提供了一种机制,通过它,突发流量可以被调整成为一个稳定的流量。

如图:

2021-01-2120-56-37.png

(4)令牌桶

令牌桶:某种程度上对漏斗算法的改进。

令牌桶能够在限制请求平均速率的同时还允许一定程度的突发调用。在令牌桶算法中,存在一个桶,用来存放固定数量的令牌。 该算法以一定的速率往桶中添加令牌。每次请求需要先获取到桶中的令牌才能继续执行,否则等待可用的令牌,或者直接拒绝。

如图:

2021-01-2120-59-20.png

  1. pom.xml 依赖
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
</dependency>
  1. 测试
    @Test
    public void test() {

        //创建一个每秒产生一个令牌的令牌桶
        RateLimiter rateLimiter = RateLimiter.create(1);

        for(int i = 1; i <= 5; i++) {

            //一次获取i个令牌
            double waitTime = rateLimiter.acquire(i);

            System.out.println("acquire:" + i + " waitTime:" + waitTime);
        }
    }