学习

如何使用 Redis 实现固定窗口限流

Brian Sam-Bodden
作者
Brian Sam-Bodden, Redis 开发者布道师

构建限流器最简单的方法是“固定窗口”实现,即我们在固定的时间窗口内限制最大请求数。例如,如果窗口大小为 1 小时,我们可以将其“固定”在整点,如 12:00-12:59、1:00-1:59 等等。

实现固定窗口限流器的过程相当简单,对于每个请求,我们需要

  1. 1.识别请求者:这可能是 API 密钥、令牌、用户的姓名或 ID,甚至是 IP 地址。
  2. 2.查找当前窗口:使用当前时间查找窗口。假设我们使用 1 小时窗口,并且现在是下午 3:15,我们可以使用 24 小时制,并将此窗口标记为“15”。
  3. 3.查找请求计数:查找请求者的固定窗口请求计数。例如,假设我们识别出请求者是 ID 为“u123”的用户,并且现在是下午 3:15。我们将查找键“u123:15”下的计数,该键下存储的值是用户 u123 从下午 3:00 到下午 3:59:59 的请求计数。
  4. 4.增加请求计数:增加窗口+用户键的请求计数。
  5. 5.适用时进行限流:如果计数超过用户的配额,则拒绝请求;否则,允许请求继续。

固定窗口方法忽略了请求的成本(所有请求都被视为相等),并且在此特定实现中,它为所有用户使用单一配额。这种简单的实现最大限度地减少了 CPU 和 I/O 利用率,但也有一些限制。由于 API 用户可能会采取“不用即失”的方式编程他们的请求,因此在窗口边缘附近可能会出现请求峰值。

一种减少这种方案中峰值的方法是使用不同粒度的多个时间窗口。例如,您可以在小时和分钟级别进行限流,例如,每小时最多允许 2,000 个请求,每分钟最多允许 33 个请求。

这个使用 Redis Strings、分钟级窗口和 20 个请求配额的基本方法在 Redis 博客上有所阐述。在我们深入 Spring Reactive 实现之前,我在这里简要总结一下

  1. 1.GET [user-api-key]:[当前分钟数] 例如 GET "u123:45"
  2. 2.如果第 1 行的结果小于 20(或者键不存在),则转到步骤 4,否则继续步骤 3
  3. 3.拒绝请求。
  4. 4.以原子方式(使用 MULTI 和 EXEC)增加键的值,并将过期时间设置为未来 59 秒。
MULTI
INCR [user-api-key]:[current minute number]
EXPIRE [user-api-key]:[current minute number] 59
EXEC

5. 否则,满足请求。

好了,既然我们知道了基本方法,让我们在 Spring 中实现它