限流之计数器算法

一 点睛

发生过载的原因主要是缓冲区满,导致处理的请求超时。所以限制流量,尽早拒绝过载状态的请求,能够保证服务尽量处理负载过程中的请求。

限流的主要方法有下面四种:

  • 计数器算法

  • 滑动窗口算法

  • 漏桶算法

  • 令牌桶算法

本篇介绍计数器算法。

二 算法

计数器算法是在一定的时间间隔里,记录请求次数,当请求次数超过该时间限制时,就把计数器清零,然后重新计算。当请求次数超过间隔内的最大次数时,拒绝访问。

例如:一个接口每分钟允许访问100次。实现方式如下:

1 设置一个计数器 count ,接收一个请求就将计数器加一,同时记录当前时间。

2 判断当前时间和上次统计时间是否为同一分钟。

如果是,则判断 count 是否超过阈值,如果超过阈值,则返回限流拒绝。

如果不是,则吧 count 重置为1,判断是否超过阈值。

如下图所示,该计数器算法要求每分钟请求的阈值不超过100个。

限流之计数器算法_第1张图片

三 代码

package currentLimit;

import java.util.Calendar;
import java.util.Date;
import java.util.Random;

/**
* @className: CounterLimit
* @description: 计数器算法
* @date: 2022/1/8
* @author: cakin
*/
public class CounterLimit {
    // 记录上次统计时间
    static Date lastDate = new Date();
    // 初始化值
    static int counter = 0;

    /**
     * 功能描述:模拟一次请求是否限流
     *
     * @return true:限流 false;不限流
     * @author cakin
     * @date 2022/1/8
     * @description:
     */
    static boolean countLimit() {
        // 获取当前时间
        Date now = new Date();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(now);
        // 当前分
        int minute = calendar.get(Calendar.MINUTE);
        calendar.setTime(lastDate);
        int lastMinute = calendar.get(Calendar.MINUTE);
        if (minute != lastMinute) {
            lastDate = now;
            counter = 0;
        }

        ++counter;
        return counter >= 100; // 判断计数器是否大于每分钟限定的值。
    }

    // 测试方法
    public static void main(String[] args) {
        for (; ; ) {
            // 模拟一秒
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Random random = new Random();
            int i = random.nextInt(3);
            // 模拟1秒内请求1次
            if (i == 1) {
                if (countLimit()) {
                    System.out.println("限流了" + counter);

                } else {
                    System.out.println("没限流" + counter);
                }
            } else if (i == 2) { // 模拟1秒内请求2次
                for (int j = 0; j < 2; j++) {
                    if (countLimit()) {
                        System.out.println("限流了" + counter);
                    } else {
                        System.out.println("没限流" + counter);
                    }
                }
            } else { // 模拟1秒内请求10次
                for (int j = 0; j < 10; j++) {
                    if (countLimit()) {
                        System.out.println("限流了" + counter);
                    } else {
                        System.out.println("没限流" + counter);
                    }
                }
            }
        }
    }
}

四 测试结果

五 算法缺陷

这种方法实现简单,但有一个明显的缺点,那就是在两个间隔之间,如果有密集的请求。则会导致单位时间内的实际请求超过阈值。

当在时间临界点的时候(比如 0:59时刻),瞬间来了 100 个请求,这时能够正常处理请求,然后在1:01时刻的时候,又来了100个请求,这时也能够正常处理这些请求。但在 2s 内,一共处理 200 个请求,可能会造成后端过载。

该算法实现简单,如果两个峰值出现在计数器清零点附近,则会造成在短时间的时间间隔类超过阈值的现象,可能导致后端过载。如下图所示,第一分钟和第二分钟的交界处分别来了100个请求,此时不会限流,灰色部分时间跨度内的请求量会让后端过载。

限流之计数器算法_第2张图片

你可能感兴趣的:(架构师,算法,架构)