如何使用 Redis 实现分布式限流?

在 Spring Cloud Gateway 中,提供了 Redis 分布式限流器的实现,具体直接看 《Spring-Cloud-Gateway 源码解析 —— 过滤器 (4.10) 之 RequestRateLimiterGatewayFilterFactory 请求限流》「5.3 Redis Lua 脚本」 部分。

另外,Redisson 库中,也提供了 Redis 分布式限流的实现,不过需要使用 Pro 版本。

? 请用 Redis 和任意语言实现一段恶意登录保护的代码,限制 1 小时内每用户 Id 最多只能登录 5 次。

这个问题,关键点,就是每个用户,每 3600 秒,只能登陆 5 次。这么一想,其实就是一个如何使用 Redis 实现限流的问题。Redis 实现限流,一共有两种方案:

  • 使用 zset 实现滑动窗口限流。代码如下:

    1. public boolean isActionAllowed(String userId, String actionKey, int period,
    2. int maxCount) {
    3. String key = String.format(""hist:%s:%s"", userId, actionKey); // 使用用户编号 + 行为作为 KEY 。这样,我们就可以统计某个用户的操作行为。
    4. long nowTs = System.currentTimeMillis(); // 获取当前时间。
    5. Pipeline pipe = jedis.pipelined(); // pipeline 批量操作,提升效率。
    6. pipe.multi(); // 此处启动了事务,可以保证指令的原子性。
    7. pipe.zadd(key, nowTs, """" + nowTs); // zset 添加,key value score 要看下。
    8. pipe.zremrangeByScore(key, 0, nowTs - (period * 1000)); // zremrangeByScore ,移除超过周期的 value 。
    9. Response<Long> count = pipe.zcard(key); // zcard ,计算 zset 的数量
    10. pipe.expire(key, period + 1); // 设置过期。这里多 + 1 秒,为了防止网络延迟。
    11. pipe.exec(); // pipeline 执行
    12. pipe.close();
    13. return count.get() <= maxCount; // 是否超过最大次数。
    14. }
    • 该实现会存在一个问题,可能一个无效的操作,也被记录到次数中。完美的话,可能需要基于 Lua 脚本实现。
    • 另外,上述代码是每秒操作的时间,实际需要改成每 N 秒。比较简单,直接上手怼即可。
  • 使用 Lua 脚本,实现令牌桶限流算法。具体可以看看 《Spring-Cloud-Gateway 源码解析 —— 过滤器 (4.10) 之 RequestRateLimiterGatewayFilterFactory 请求限流》 的源码解析。

  • 使用 Lua 脚本,实现简单的滑动窗口。