滑动窗口限流是一种流量控制策略,通过维护一个滑动时间窗口来限制单位时间内允许的操作数量。该策略能平滑控制流量,相比固定速率限制更加灵活,能有效应对突发流量而不浪费系统资源。

基于Redis的滑动窗口限流实现方案

原理说明

  • 利用时间窗口记录请求时间戳
  • 统计窗口内的请求总数来判断是否超出限制
  • 自动清理窗口外的历史数据

 Redis实现方案 推荐使用ZSET数据结构

  • key:限流的资源标识(如接口名)
  • score:请求时间戳
  • member:请求唯一标识(建议使用UUID/MD5避免并发冲突)

示例

以登录接口1分钟100次调用为例,实现步骤如下:

  1. 记录当前请求时间戳
  2. 清理60秒前的历史数据
  3. 统计当前窗口内请求数
  4. 未超限则记录本次请求

基础实现代码示例:

import redis.clients.jedis.Jedis;

public class SlidingWindowRateLimiter {
    private Jedis jedis;
    private String key;
    private int limit;

    public boolean allowRequest(String key) {
        long currentTime = System.currentTimeMillis();
        long windowStart = currentTime - 60 * 1000;
        
        jedis.zremrangeByScore(key, "-inf", String.valueOf(windowStart));
        long currentRequests = jedis.zcard(key);
        
        if (currentRequests < limit) {
            jedis.zadd(key, currentTime, String.valueOf(currentTime));
            return true;
        }
        return false;
    }
}

原子性优化方案(使用Lua脚本):

import redis.clients.jedis.Jedis;

public class SlidingWindowRateLimiter {
    private Jedis jedis;
    private String key;
    private int limit;

    public boolean allowRequest(String key) {
        long currentTime = System.currentTimeMillis();
        
        String luaScript = "local window_start = ARGV[1] - 60000\n" +
                          "redis.call('ZREMRANGEBYSCORE', KEYS[1], '-inf', window_start)\n" +
                          "local current_requests = redis.call('ZCARD', KEYS[1])\n" +
                          "if current_requests < tonumber(ARGV[2]) then\n" +
                          "    redis.call('ZADD', KEYS[1], ARGV[1], ARGV[1])\n" +
                          "    return 1\n" +
                          "else\n" +
                          "    return 0\n" +
                          "end";
        
        Object result = jedis.eval(luaScript, 1, key, String.valueOf(currentTime), String.valueOf(limit));
        return (Long) result == 1;
    }
}

相关知识

Redis命令说明

  • ZREMRANGEBYSCORE:移除指定分数区间的成员
  • ZCARD:获取有序集合成员数量
  • -inf表示负无穷,用于清理所有历史数据

基于Redisson实现限流

在 Redisson框架中,已经给我们提供了一个限流器——RRateLimiter,不过他并不是滑动窗口,而是一个令牌桶的算法。具体的可以参考:Redisson分布式限流器RRateLimiter原理解析

使用方式如下:

public Boolean tryAcquire(String key, int limit, int windowSize) {
    RRateLimiter rRateLimiter = redissonClient.getRateLimiter(LIMIT_KEY_PREFIX + key);
    
    if (!rRateLimiter.isExists()) {
        rRateLimiter.trySetRate(RateType.OVERALL, limit, windowSize, RateIntervalUnit.SECONDS);
    }
    
    return rRateLimiter.tryAcquire();
}

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐