Principle and Implementation of Current Limiting Algorithm

What is the current limiting algorithm

Current limiting refers to limiting the access of new traffic to the system in the case of high concurrency and large traffic requests, so as to ensure the security of system services.

Application Scenario

Reasons for excessive requests:

  • Burst requests brought by hotspot services
  • Burst requests caused by caller bugs
  • malicious attack request

Generally speaking, the seckill system has such a scenario. When the seckill is enabled, the server is under too much pressure due to frequent access requests, which may lead to a crash

  • Therefore, a window current limiting algorithm can be used to limit each user to visit N times within a certain period of time. When the number of visits is exceeded, it will be temporarily added to the blacklist and an expiration time can be set, which can be implemented in combination with Redis

Principle of Current Limiting Algorithm

Fixed window current limiting algorithm

Fixed window: That is, the message is fixed within the time interval, which makes it impossible to prevent burst traffic.
Requests exceeding the window will be discarded
insert image description here

Code:

    /**
     * 固定窗口限流算法
     * 阈值为 3
     * 时间窗口为 1 s
     */
    static final int THRESHOLD = 3;
    static final long window = 1000;
    static long startTime = System.currentTimeMillis();
    static int count = 0;
    static boolean fixedWindow(){
    
    
        // 当前时间
        long curTime = System.currentTimeMillis();
        // 判断当前时间是否还在区间内
        if(curTime - window > startTime){
    
    
            // 更新开始时间
            startTime = curTime;
            // 重置计数
            count = 0;
        }
        // 判断请求是否达到阈值
        if(count < THRESHOLD){
    
    
            count++;
            return true;
        }
        // 请求已经达到阈值
        return false;
    }


    /**
     * 简单测试
     */
    public static void main(String[] args) throws InterruptedException {
    
    
        // 模拟 1秒 内无限请求
        long startTime = System.currentTimeMillis();
        int index = 0;
        while(true){
    
    
            long endTime = System.currentTimeMillis();
            // 如果提前跳出
            if(endTime - startTime >= 900){
    
    
                break;
            }
            Thread.sleep(50);
            System.out.println( index++ + (fixedWindow()? "允许请求" : "拒绝请求"));
        }

        // 1 秒后再请求
        Thread.sleep(100);
        System.out.println("一秒后" + (fixedWindow()? "允许请求" : "拒绝请求"));
    }

Test Results:

0允许请求
1允许请求
2允许请求
3拒绝请求
4拒绝请求
5拒绝请求
6拒绝请求
7拒绝请求
8拒绝请求
9拒绝请求
10拒绝请求
11拒绝请求
12拒绝请求
13拒绝请求
14拒绝请求
一秒后允许请求

The situation of burst traffic: it is also a critical problem, that is, 1秒3 messages are sent before and after the present, which obviously violates the
insert image description here

Sliding window current limiting algorithm

The sliding window algorithm is similar to the fixed window algorithm, but instead 全部更新, 部分更新only one window is updated at a time, but the problem is that when the current limit is reached, the request will be violently rejected, and the sliding distance of the window is relatively slow

It is more flexible and can deal with the problem of sudden traffic

  • The figure below updates a window every 0.5 seconds. Obviously, 1 ~ 1.5 3 requests within this time period will not exceed the threshold, but if there are unlimited requests before and after 1.9 ~ 2the refresh , in the situation shown in the figure below, only one more will be received Requests, which are slightly above the threshold, can be made smaller by finer settings.
  • Compared with the fixed window, the sliding window obviously relieves the pressure on the problem of burst traffic

Graphic:
insert image description here

Implementation code:

    /**
     * 滑动窗口限流算法
     * 窗口初始为 10
     * 时间窗口为 0.5 s
     */
    // 窗口大小
    static int size = 10;
    // 窗口 大小:10
    static boolean[] window = new boolean[size];
    // 窗口指针索引
    static int index = 0;
    // 上一个开始时间
    static long startTime = System.currentTimeMillis();
    // 时间区间 1s
    static long time = 1000;
    // 当前窗口计数总和
    static long counter = 0;
    static boolean slideWindow(){
    
    
        // 当前时间
        long curTime = System.currentTimeMillis();
        // 更新窗口(这个更新窗口应该是异步更新才对)
        if(curTime - startTime > time){
    
    
            // 更新开始时间
            startTime = curTime;
            // 更新窗口为可使用
            window[index] = false;
            counter--;
        }
        // 判断当前窗口是否可用
        if(!window[index]){
    
    
            // 记录窗口已被使用
            window[index] = true;
            // 移动到下一个节点 (超出大小求余)
            index++;
            index = index % size;
            // 计数
            counter++;
            return true;
        }
        // 请求已经达到阈值
        return false;
    }

	/**
	*  简单测试
 	*/
    public static void main(String[] args) throws InterruptedException {
    
    
        // 模拟 1秒 内无限请求
        long startTime = System.currentTimeMillis();
        int index = 0;
        while(true){
    
    
            long endTime = System.currentTimeMillis();
            // 提前跳出,以防最后一个方法调用的时候实际计时已经超过 1秒
            if(endTime - startTime >= 900){
    
    
                break;
            }
            // 睡眠 50ms
            Thread.sleep(50);
            System.out.println( index++ + (slideWindow()? "允许请求" : "拒绝请求"));
        }

        // 1 秒后再请求
        Thread.sleep(100);
        System.out.println("一秒后" + (slideWindow()? "允许请求" : "拒绝请求"));
    }

test:

0允许请求
1允许请求
2允许请求
3允许请求
4允许请求
5允许请求
6允许请求
7允许请求
8允许请求
9允许请求
10拒绝请求
11拒绝请求
12拒绝请求
13拒绝请求
14拒绝请求
一秒后允许请求

Leaky Bucket Current Limiting Algorithm

The bucket leak algorithm is different from the previous two algorithms. It requires smooth processing to ensure an overall rate

Principle : It is to fill the bucket with water. The rate of this water injection can be 任意fast. If it exceeds the capacity of the bucket, it needs to be discarded. Because the capacity of the bucket is 不变the largest, the processing rate is also 固定the largest, so the overall rate is guaranteed

  • Compared with the previous two, I may not use the window, and the rate can be zero, but the water droplets in the bucket will definitely leak , so the bucket leak is a constant rate

The problem of burst traffic can also be solved, because the size of the bucket is fixed and has nothing to do with the burst of time. Even if the number of requests increases, the processing rate does not change.

Graphic:
insert image description here

Implementation code:

    /**
     * 桶漏限流算法
     */
    // 桶的容量
    static long capacity = 10;
    // 当前水量
    static long currWater = 0;
    // 上次的结束时间(初始化)
    static long startTime = System.currentTimeMillis();
    // 出水速率 (1 滴/s)
    static long outputRate = 1;
    static boolean bucketLimit(){
    
    
        // 获取当前时间
        long curTime = System.currentTimeMillis();
        // 更新出水量
        if(curTime - startTime > 1000){
    
    
            // 计算出水量 = (结束时间 - 开始时间) / 1000 * 出水速率
            System.out.println(curTime - startTime);
            long outputWater = (curTime - startTime) / 1000 * outputRate;
            // 计算剩余水量 = 当前水量 - 出水量
            currWater = Math.max(0, currWater - outputWater);
            // 更新时间
            startTime = curTime;
        }
        // 判断桶是否满了
        if(currWater < capacity){
    
    
            // 未满,请求通过,当前水量 + 1
            currWater++;
            return true;
        }

        // 桶满,拒绝请求
        return false;
    }


    public static void main(String[] args) throws InterruptedException {
    
    
        // 模拟 1秒 内无限请求
        long startTime = System.currentTimeMillis();
        int index = 0;
        while(true){
    
    
            long endTime = System.currentTimeMillis();
            // 如果提前跳出
            if(endTime - startTime >= 900){
    
    
                break;
            }
            Thread.sleep(50);
            System.out.println( index++ + (bucketLimit()? "允许请求" : "拒绝请求"));
        }

        // 1 秒后再请求
        Thread.sleep(100);
        System.out.println("一秒后" + (bucketLimit()? "允许请求" : "拒绝请求"));
    }

Test Results:

0允许请求
1允许请求
2允许请求
3允许请求
4允许请求
5允许请求
6允许请求
7允许请求
8允许请求
9允许请求
10拒绝请求
11拒绝请求
12拒绝请求
13拒绝请求
14拒绝请求
15拒绝请求
1047
一秒后允许请求

Token Bucket Current Limiting Algorithm

Both the Token Bucket Algorithm and the Bucket Leaky Algorithm have the same protagonists , but what is contained in the bucket is 令牌, which is the opposite of the bucket leak, where the token is issued

  • Generate tokens at a constant rate
  • Processing speed is arbitrary

The token bucket can store a certain number of tokens. Similarly, when this number is exceeded, the token also needs to be discarded. When a request comes, it is necessary to determine whether a token can be obtained from the bucket. If it can be obtained, it means can be accessed.

The problem of burst traffic can also be solved. When burst traffic comes, requests can only be processed according to the number of token buckets. Extra requests cannot be processed without corresponding tokens.

Graphic:
insert image description here

Implementation code:

    /**
     * 令牌桶限流算法
     */
    // 令牌桶的容量
    static long capacity = 10;
    // 当前令牌数
    static long tokens = 10;
    // 上次的结束时间(初始化)
    static long startTime = System.currentTimeMillis();
    // 产生令牌速率 (1 个/s)
    static long inputRate = 1;
    static boolean tokenBucketLimit(){
    
    
        // 获取当前时间
        long curTime = System.currentTimeMillis();
        // 更新令牌数
        if(curTime - startTime > 1000){
    
    
            // 计算出水量 = (结束时间 - 开始时间) / 1000 * 出水速率
            System.out.println(curTime - startTime);
            long token = (curTime - startTime) / 1000 * inputRate;
            // 计算剩余水量 = 当前水量 - 出水量
            tokens = Math.min(capacity, tokens + token);
            // 更新时间
            startTime = curTime;
        }
        // 判断令牌桶中是否有令牌
        if(tokens > 0){
    
    
            // 颁发令牌,请求通过
            tokens--;
            return true;
        }

        // 没有令牌,拒绝请求
        return false;
    }


    /**
     * 简单测试
     */
    public static void main(String[] args) throws InterruptedException {
    
    
        // 模拟 1秒 内无限请求
        long startTime = System.currentTimeMillis();
        int index = 0;
        while(true){
    
    
            long endTime = System.currentTimeMillis();
            // 如果提前跳出
            if(endTime - startTime >= 900){
    
    
                break;
            }
            Thread.sleep(50);
            System.out.println( index++ + (tokenBucketLimit()? "允许请求" : "拒绝请求"));
        }

        // 1 秒后再请求
        Thread.sleep(100);
        System.out.println("一秒后" + (tokenBucketLimit()? "允许请求" : "拒绝请求"));
    }

Test Results:

0允许请求
1允许请求
2允许请求
3允许请求
4允许请求
5允许请求
6允许请求
7允许请求
8允许请求
9允许请求
10拒绝请求
11拒绝请求
12拒绝请求
13拒绝请求
14拒绝请求
15拒绝请求
1018
一秒后允许请求

Guess you like

Origin blog.csdn.net/DespairC/article/details/126765665