Simple java implementation of sliding time window current limiting algorithm

Searching the sliding time window current limit algorithm on the Internet, most of them are too complicated, I have implemented a simple one, first add the code:

import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 滑动时间窗口限流工具
 * 本限流工具只适用于单机版,如果想要做全局限流,可以按本程序的思想,用redis的List结构去实现
 *
 * @author dijia478
 * @date 2020-10-13 10:53
 */
public class SlideWindow {

    /** 队列id和队列的映射关系,队列里面存储的是每一次通过时候的时间戳,这样可以使得程序里有多个限流队列 */
    private volatile static Map<String, List<Long>> MAP = new ConcurrentHashMap<>();

    private SlideWindow() {}

    public static void main(String[] args) throws InterruptedException {
        while (true) {
            // 任意10秒内,只允许2次通过
            System.out.println(LocalTime.now().toString() + SlideWindow.isGo("ListId", 2, 10000L));
            // 睡眠0-10秒
            Thread.sleep(1000 * new Random().nextInt(10));
        }
    }

    /**
     * 滑动时间窗口限流算法
     * 在指定时间窗口,指定限制次数内,是否允许通过
     *
     * @param listId     队列id
     * @param count      限制次数
     * @param timeWindow 时间窗口大小
     * @return 是否允许通过
     */
    public static synchronized boolean isGo(String listId, int count, long timeWindow) {
        // 获取当前时间
        long nowTime = System.currentTimeMillis();
        // 根据队列id,取出对应的限流队列,若没有则创建
        List<Long> list = MAP.computeIfAbsent(listId, k -> new ArrayList<>());
        // 如果队列还没满,则允许通过,并添加当前时间戳到队列开始位置
        if (list.size() < count) {
            list.add(0, nowTime);
            return true;
        }

        // 队列已满(达到限制次数),则获取队列中最早添加的时间戳
        Long farTime = list.get(count - 1);
        // 用当前时间戳 减去 最早添加的时间戳
        if (nowTime - farTime <= timeWindow) {
            // 若结果小于等于timeWindow,则说明在timeWindow内,通过的次数大于count
            // 不允许通过
            return false;
        } else {
            // 若结果大于timeWindow,则说明在timeWindow内,通过的次数小于等于count
            // 允许通过,并删除最早添加的时间戳,将当前时间添加到队列开始位置
            list.remove(count - 1);
            list.add(0, nowTime);
            return true;
        }
    }

}

 It can be seen that in any 10 seconds, the number of passes does not exceed 2 times. Or according to the implementation principle, the time difference between any two passes does not exceed 10 seconds:

 

Here is a drawing to explain why the sliding window current limit can be achieved in this way, assuming that 5 passes are allowed within 10 seconds

1. This line is the queue list. When the first event comes in, the queue size is 0 and the time is the first second:

 

 2. Because size=0, less than 5, there is no limit to the number of times, no need to consider the time window, directly put the timestamp of this event to the position of 0:

 

3. At 2.8 seconds, the second event came. Because size=1 at this time, it is still less than 5, and the timestamp of this event is placed at the position of 0. The original timestamp of the event from the first second will move one space back:

 

4. Three more events came one after another, the queue size became 5, and the timestamps that came first moved backward in turn. At this time, the sixth event has arrived, and the time is the eighth second:

 

5. Because size=5, not less than 5, the limit has been reached at this time, and the time window needs to be considered in the future. So take the time at position 4 (the farthest time from now) and compare it with the timestamp of the 6th event:

 

6. The difference obtained is 7 seconds, which is less than 10 seconds of the time window, indicating that the number of incoming events in 10 seconds is greater than 5, so this time it is not allowed to pass:

 

7. Even if there are 100 events in the next, as long as the time difference is less than or equal to 10 seconds, they will all be the same as above and refuse to pass:

 

8. In 11.1 seconds, the 101st incident came. Because size=5, not less than 5, take out the time at position 4 (the farthest time from now) and compare it with the timestamp of the 101st event:

 

9. The difference obtained is 10.1 seconds, which is greater than the time window of 10 seconds, indicating that the number of events in 10 seconds is less than or equal to 5, so this time it is allowed to pass:

 

10. Delete the time at position 4 (the farthest time from the present), put the time stamp of this event to 0, and the subsequent time stamps move backward in turn:

 

For other events in the future, you can repeat the steps 4-10 to achieve this. In any sliding time window, the number of passes is limited.

The essential idea is to change the concept, and limit the number of times the original problem is determined. Converted to a certain number of times, and time limit.

Guess you like

Origin blog.csdn.net/a159357445566/article/details/109062296