滑动窗口算法

发布于:2025-06-21 ⋅ 阅读:(20) ⋅ 点赞:(0)

概述

滑动窗口算法(Sliding Window Algorithm)是一种常用的限流(Rate Limiting)技术,广泛应用于接口防刷、API限流、网络流量控制等场景。它的核心思想是:在一个固定长度的时间窗口内,统计请求的数量,如果超过了设定的阈值,则拒绝后续请求,否则允许通过。

原理

窗口定义

设定一个时间窗口(如1分钟),窗口内最多允许N次请求。

窗口滑动

窗口不是固定的,而是随着时间的推移不断“滑动”。每次有新请求到来时,窗口的起止时间会根据当前时间动态调整。

请求统计

统计当前窗口内的请求数。如果请求数未超过阈值,则允许通过;否则拒绝。

实现

代码定义

 @Autowired
    private RedisTemplate<String, Object> redisTemplate;

 /**
     * 是否能通过滑动窗口的验证
     * 滑动窗口算法:
     *     描述:滑动窗口校验算法是一种用于数据流处理的高效技术,特别适用于网络通信、数据压缩和实时系统等领域。它通过维护一个动态窗口来校验数据的完整性或检测重复数据。
     *     核心概念:
     *      1. 窗口定义:固定大小的缓冲区,用于存储最近处理的数据
     *      2. 窗口滑动:新数据加入时,最旧的数据被移出窗口
     *      3. 校验机制:对窗口内的数据进行特定校验(如CRC、哈希或重复检测)
     * @param key 事件标识
     * @param windowPeriod 窗口限流的周期,单位是毫秒
     * @param windowSize 滑动窗口大小
     * @return
     */
 public boolean passThough(String key, long windowPeriod, int windowSize) {
        //风控key
        String riskControlKey = IMPlatformConstants.getKey(IMPlatformConstants.RISK_CONTROL_KEY_PREFIX, key);
        //获取当前时间
        long currentTimeStamp = System.currentTimeMillis();
        // 表示整个滑动窗口的总时长,也就是系统会在这个时长内统计请求数量,判断是否超过限流阈值。
        long length = windowPeriod * windowSize;
        long start = currentTimeStamp - length;
        //计算过期时间
        long expireTime = length + windowPeriod;
        redisTemplate.opsForZSet().add(riskControlKey, String.valueOf(currentTimeStamp), currentTimeStamp);
        // 移除[0,start]区间内的值
        redisTemplate.opsForZSet().removeRangeByScore(riskControlKey, 0, start);
        // 获取窗口内元素个数
        Long count = redisTemplate.opsForZSet().zCard(riskControlKey);
        // 过期时间 窗口长度+一个时间间隔
        redisTemplate.expire(riskControlKey, expireTime, TimeUnit.MILLISECONDS);
        //count为空不能通过
        if (count == null) {
            return false;
        }
        return count <= windowSize;
    }

可以看到,passThough 方法基于Redis 容器实现了一个滑动窗口算法。

示例

IP限流控制

比如:我们现在有一个需求,需要根据IP 进行限流控制 1秒内同一个IP 只允许访问10 次,那么调用

public static void main(String[] args) {
        RedisSlidingWindowLimitService limitService = new RedisSlidingWindowLimitService();
        // 需要限制的IP地址, 实际业务动态获取
        String ip = "127.0.0.1";
        // 窗口限流的最小周期,单位是毫秒,计算方式就是 windowPeriod = 限制周期 / 窗口大小,在这里就是 1000 / 10 = 100;
        long windowPeriod = 100;
        // 滑动窗口大小, 也就是限制的数量
        int windowSize = 10;
        boolean result = limitService.passThough(ip, windowPeriod, 10);
        if (!result) {
            System.out.println("当前IP访问达到阈值,不允许访问了!");
        }

    }

注意点:算法中 length 代表需要统计窗口的周期,我们的需求是 1秒内同一个IP最大访问10次, 所以 windowPeriod = 1000(毫秒) / 10;


网站公告

今日签到

点亮在社区的每一天
去签到