throttling.py

限制

HTTP/1.1 420 增强你的冷静

Twitter API 限流响应

限流类似于权限,因为它确定是否应授权请求。限流表示临时状态,用于控制客户端可以向 API 发出的请求速率。

与权限一样,可以使用多个限流。你的 API 可能对未经身份验证的请求有严格的限流,对经过身份验证的请求有较不严格的限流。

你可能希望使用多个限流的另一个场景是,由于某些服务特别占用资源,你需要对 API 的不同部分施加不同的约束。

如果你想同时施加突发限流速率和持续限流速率,也可以使用多个限流。例如,你可能希望将用户限制为每分钟最多 60 个请求,每天最多 1000 个请求。

限流不一定只指限流请求。例如,存储服务可能还需要针对带宽进行限流,付费数据服务可能希望针对访问的记录数进行限流。

**REST 框架提供的应用程序级限流不应被视为安全措施或防止暴力破解或拒绝服务攻击的保护。故意恶意行为者始终能够欺骗 IP 来源。除此之外,内置限流实现是使用 Django 的缓存框架实现的,并使用非原子操作来确定请求速率,这有时可能导致一些模糊性。**

REST 框架提供的应用程序级限流旨在实施诸如不同业务层和针对服务过度使用的基本保护之类的策略。**

如何确定限流

与权限和身份验证一样,REST 框架中的限流始终定义为一个类列表。

在运行视图的主体之前,将检查列表中的每个节流。如果任何节流检查失败,将引发exceptions.Throttled异常,并且视图的主体将不会运行。

设置限流策略

可以使用DEFAULT_THROTTLE_CLASSESDEFAULT_THROTTLE_RATES设置在全局范围内设置默认节流策略。例如。

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle'
    ],
    'DEFAULT_THROTTLE_RATES': {
        'anon': '100/day',
        'user': '1000/day'
    }
}

DEFAULT_THROTTLE_RATES中使用的速率描述可以包括secondminutehourday作为节流周期。

您还可以使用基于类的APIView视图,对每个视图或每个视图集设置节流策略。

from rest_framework.response import Response
from rest_framework.throttling import UserRateThrottle
from rest_framework.views import APIView

class ExampleView(APIView):
    throttle_classes = [UserRateThrottle]

    def get(self, request, format=None):
        content = {
            'status': 'request was permitted'
        }
        return Response(content)

如果您将@api_view装饰器与基于函数的视图一起使用,则可以使用以下装饰器。

@api_view(['GET'])
@throttle_classes([UserRateThrottle])
def example_view(request, format=None):
    content = {
        'status': 'request was permitted'
    }
    return Response(content)

还可以为使用@action装饰器创建的路由设置节流类。以这种方式设置的节流类将覆盖任何视图集级别的类设置。

@action(detail=True, methods=["post"], throttle_classes=[UserRateThrottle])
def example_adhoc_method(request, pk=None):
    content = {
        'status': 'request was permitted'
    }
    return Response(content)

如何识别客户端

X-Forwarded-For HTTP 头和 REMOTE_ADDR WSGI 变量用于唯一标识用于节流的客户端 IP 地址。如果存在 X-Forwarded-For 头,则将使用它,否则将使用 WSGI 环境中 REMOTE_ADDR 变量的值。

如果您需要严格标识唯一的客户端 IP 地址,则需要首先通过设置 NUM_PROXIES 设置来配置 API 运行在其后面的应用程序代理的数量。此设置应为零或更多整数。如果设置为非零,则客户端 IP 将被标识为 X-Forwarded-For 头中的最后一个 IP 地址,一旦首先排除了任何应用程序代理 IP 地址。如果设置为零,则 REMOTE_ADDR 值将始终用作标识 IP 地址。

了解这一点很重要,如果您配置了 NUM_PROXIES 设置,则唯一 NAT'd 网关后面的所有客户端都将被视为单个客户端。

有关 X-Forwarded-For 头的工作原理以及如何识别远程客户端 IP 的更多背景信息,请 在此处 查找。

设置缓存

REST 框架提供的节流类使用 Django 的缓存后端。您应该确保已设置适当的 缓存设置LocMemCache 后端的默认值对于简单的设置应该没问题。有关更多详细信息,请参阅 Django 的 缓存文档

如果您需要使用除 'default' 之外的其他缓存,可以通过创建自定义节流类并设置 cache 属性来实现。例如

from django.core.cache import caches

class CustomAnonRateThrottle(AnonRateThrottle):
    cache = caches['alternate']

您需要记住在 'DEFAULT_THROTTLE_CLASSES' 设置键中或使用 throttle_classes 视图属性中设置您的自定义节流类。

关于并发性的说明

内置的节流实现对竞争条件开放,因此在高并发下,它们可能允许通过一些额外的请求。

如果你的项目依赖于在并发请求期间保证请求数量,则需要实现自己的节流类。有关更多详细信息,请参阅问题 #5181


API 参考

AnonRateThrottle

AnonRateThrottle 将仅对未经身份验证的用户进行节流。传入请求的 IP 地址用于生成唯一的密钥以进行节流。

允许的请求速率由以下内容之一确定(按优先顺序排列)。

  • 类上的 rate 属性,可以通过覆盖 AnonRateThrottle 并设置属性来提供。
  • DEFAULT_THROTTLE_RATES['anon'] 设置。

如果你想限制来自未知来源的请求速率,则 AnonRateThrottle 是合适的。

UserRateThrottle

UserRateThrottle 将以给定的请求速率对 API 中的用户进行节流。用户 ID 用于生成唯一的密钥以进行节流。未经身份验证的请求将退回到使用传入请求的 IP 地址来生成唯一的密钥以进行节流。

允许的请求速率由以下内容之一确定(按优先顺序排列)。

  • 类上的 rate 属性,可以通过覆盖 UserRateThrottle 并设置属性来提供。
  • DEFAULT_THROTTLE_RATES['user'] 设置。

一个 API 可以在同一时间有多个 UserRateThrottles。为此,请覆盖 UserRateThrottle 并为每个类设置唯一的“范围”。

例如,可以使用以下类实现多个用户节流速率...

class BurstRateThrottle(UserRateThrottle):
    scope = 'burst'

class SustainedRateThrottle(UserRateThrottle):
    scope = 'sustained'

...以及以下设置。

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        'example.throttles.BurstRateThrottle',
        'example.throttles.SustainedRateThrottle'
    ],
    'DEFAULT_THROTTLE_RATES': {
        'burst': '60/min',
        'sustained': '1000/day'
    }
}

如果你想要每个用户简单的全局速率限制,则 UserRateThrottle 是合适的。

ScopedRateThrottle

ScopedRateThrottle 类可用于限制对 API 特定部分的访问。仅当正在访问的视图包含 .throttle_scope 属性时,才会应用此节流。然后,将通过将请求的“范围”与唯一用户 ID 或 IP 地址连接起来来形成唯一的节流密钥。

允许的请求速率由 DEFAULT_THROTTLE_RATES 设置确定,使用请求“范围”中的密钥。

例如,给定以下视图...

class ContactListView(APIView):
    throttle_scope = 'contacts'
    ...

class ContactDetailView(APIView):
    throttle_scope = 'contacts'
    ...

class UploadView(APIView):
    throttle_scope = 'uploads'
    ...

...以及以下设置。

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.ScopedRateThrottle',
    ],
    'DEFAULT_THROTTLE_RATES': {
        'contacts': '1000/day',
        'uploads': '20/day'
    }
}

ContactListViewContactDetailView 的用户请求将限制为每天总共 1000 个请求。对 UploadView 的用户请求将限制为每天 20 个请求。


自定义限流

要创建自定义节流,请覆盖 BaseThrottle 并实现 .allow_request(self, request, view)。如果应该允许请求,则该方法应返回 True,否则返回 False

你还可以选择覆盖 .wait() 方法。如果已实现,则 .wait() 应返回在尝试下一次请求之前建议等待的秒数,或 None。仅当 .allow_request() 之前已返回 False 时,才会调用 .wait() 方法。

如果实现了 .wait() 方法并且请求受到限制,则响应中将包含 Retry-After 标头。

示例

以下是速率限制的示例,它将随机限制每 10 个请求中的 1 个。

import random

class RandomRateThrottle(throttling.BaseThrottle):
    def allow_request(self, request, view):
        return random.randint(1, 10) != 1