如何使用 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 实现滑动窗口限流。代码如下:
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; // 是否超过最大次数。
}
- 该实现会存在一个问题,可能一个无效的操作,也被记录到次数中。完美的话,可能需要基于 Lua 脚本实现。
- 另外,上述代码是每秒操作的时间,实际需要改成每 N 秒。比较简单,直接上手怼即可。
使用 Lua 脚本,实现令牌桶限流算法。具体可以看看 《Spring-Cloud-Gateway 源码解析 —— 过滤器 (4.10) 之 RequestRateLimiterGatewayFilterFactory 请求限流》 的源码解析。
使用 Lua 脚本,实现简单的滑动窗口。