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