速率限制

Frappe 框架开箱即用地支持对 HTTP 请求进行速率限制。

Frappe 框架基于请求所消耗的时间实现固定窗口速率限制。该限制针对在配置窗口内所有 HTTP 请求所花费时间之和进行强制约束。每经过 window 秒后周期会重置,例如,将 window 设置为 3600 秒,将根据站点所在时区在每个小时开始时将使用计数器重置为 0。

注意:超过限制的请求不会被处理,并会返回 HTTP 429(请求过多)响应。

你可以通过在 site_config.json 中添加类似以下配置来为你的站点启用速率限制:

{
 "rate_limit": {
 "limit": 600,
 "window": 3600
 }
}
键 描述
limit 在速率限制窗口内允许使用的最大时间量(以秒为单位)。
window 速率限制窗口的大小(以秒为单位)。

每个 HTTP 请求返回的 HTTP 头都会显示当前的速率限制状态,例如

curl -i https://frappe.io/docs
HTTP/1.1 200 OK
X-RateLimit-Limit: 600000000
X-RateLimit-Remaining: 518060453
X-RateLimit-Reset: 3513
X-RateLimit-Used: 100560

如果在配置的限制耗尽后仍有请求发出,则会返回 HTTP 429 响应以及速率限制状态:

curl -i https://frappe.io/docs
HTTP/1.1 429 TOO MANY REQUESTS
X-RateLimit-Limit: 600000000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1242
Retry-After: 1242
头 描述
Retry-After 当前速率限制窗口重置前剩余的时间(以秒为单位)。
X-RateLimit-Limit 在一个速率限制窗口内允许使用的时间(以微秒为单位)。
X-RateLimit-Remaining 当前速率限制窗口中剩余(可使用)的时间(以微秒为单位)。
X-RateLimit-Reset 当前速率限制窗口重置前剩余的时间(以秒为单位)。
X-RateLimit-Used 用于处理当前请求所使用的时间(以微秒为单位)。

特定端点的速率限制

站点范围的配置限制的是请求的总处理时间,而开发者可以使用位于 frappe.rate_limiter 中的 @rate_limit 装饰器,对特定的白名单 Python 方法应用严格的请求次数限制。

装饰器参数:

参数 类型 默认值 描述
key str None 来自 frappe.form_dict 的特定键,用于唯一标识该请求。
limit int 或 Callable 5 在时间窗口内允许的最大请求数。
seconds int 86400 时间窗口,以秒为单位(默认是 24 小时)。
methods str 或 list "ALL" 要进行速率限制的 HTTP 方法(例如 "POST" 或 ["GET", "POST"])。
ip_based bool True 按请求者的 IP 地址进行限制跟踪(网络感知)。
endpoint str None (在 v17 中添加)自定义计数器名称。如果可调用对象没有稳定的点分路径(例如 functools.partial),则此项为必填。
user_based bool False (在 v17 中添加)按已认证用户的会话 ID 进行限制跟踪(身份感知)。

跟踪行为与互斥性 (身份感知跟踪和安全访客回退从版本 17 开始可用。在 v15 或 v16 中使用 user_based 或 endpoint 会引发 TypeError)。

速率限制器旨在按网络身份或用户身份来跟踪请求,而不会同时按两者跟踪。

  • 身份感知(user_based=True):限制严格应用于已认证的 frappe.session.user。请求者的 IP 地址会被完全忽略,从而允许共享企业网络(NAT)中的用户避免无意中互相阻塞。
  • 网络感知(ip_based=True, user_based=False):限制严格应用于 request_ip。用户的会话 ID 会被忽略。这是默认的旧版行为。
  • 访客回退:如果某个端点配置了 user_based=True,未认证的“访客”流量将安全地回退为按其 IP 地址进行跟踪。这可以防止单个恶意访客耗尽全局访客配额,从而导致所有其他匿名用户遭遇拒绝服务。

示例实现:

from frappe.rate_limiter import rate_limit

# Limits authenticated users to 10 requests per minute per account.
# Guests hitting this mixed-auth endpoint are limited to 10 requests per minute per IP.
@frappe.whitelist()
@rate_limit(limit=10, seconds=60, user_based=True)
def generate_heavy_report():
    pass