文章

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

如何使用 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,                               int maxCount) {    String key = String.format("hist:%s:%s", userId, actionKey); // 使用用户编号 + 行为作为 KEY 。这样,我们就可以统计某个用户的操作行为。    long nowTs = System.currentTimeMillis(); // 获取当前时间。    Pipeline pipe = jedis.pipelined(); // pipeline 批量操作,提升效率。    pipe.multi(); // 此处启动了事务,可以保证指令的原子性。    pipe.zadd(key, nowTs, "" + nowTs); // zset 添加,key value score 要看下。    pipe.zremrangeByScore(key, 0, nowTs - (period * 1000)); // zremrangeByScore ,移除超过周期的 value 。    Response<Long> count = pipe.zcard(key); // zcard ,计算 zset 的数量    pipe.expire(key, period + 1); // 设置过期。这里多 + 1 秒,为了防止网络延迟。    pipe.exec(); // pipeline 执行    pipe.close();    return count.get() <= maxCount; // 是否超过最大次数。}

```plain text

  • 该实现会存在一个问题,可能一个无效的操作,也被记录到次数中。完美的话,可能需要基于 Lua 脚本实现。
  • 另外,上述代码是每秒操作的时间,实际需要改成每 N 秒。比较简单,直接上手怼即可。

    ```

  • 使用 Lua 脚本,实现令牌桶限流算法。具体可以看看艿艿对 《Spring-Cloud-Gateway 源码解析 —— 过滤器 (4.10) 之 RequestRateLimiterGatewayFilterFactory 请求限流》 的源码解析。
  • 使用 Lua 脚本,实现简单的滑动窗口。具体可以看看艿艿对 《精尽 Redisson 源码分析 —— 限流器 RateLimiter》 的源码解析。
本文由作者按照 CC BY 4.0 进行授权