学习

如何使用 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 字符串、分钟大小的窗口和 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 中实现它。