Django REST framework - 限流

概述

就像权限控制一样,限流用于决定是否授权请求。限流表示一种临时状态,用于控制客户端可以向 API 发送请求的速率。就像权限控制一样,可以使用多个限流策略。例如,你的 API 可能对未认证请求设置严格的限流策略,而对已认证请求设置宽松的限流策略。如果不同部分的 API 由于某些服务资源消耗较高,需要施加不同的限制,也可以使用多个限流策略。此外,还可以同时使用突发流量限流和持续流量限流,例如将用户限制为每分钟最多 60 次请求,每天最多 1000 次请求。

限流并不一定仅指限制请求速率。例如,存储服务可能需要对带宽进行限流,而付费数据服务可能需要对访问记录数量进行限制。需要注意的是,REST framework 提供的应用级限流不应被视为安全措施或防止暴力破解和拒绝服务攻击的手段。恶意用户总是可以伪造 IP 地址。此外,内置的限流实现使用 Django 缓存框架,并使用非原子操作来确定请求速率,这可能导致一些不精确性。REST framework 提供的应用级限流旨在实施不同业务层级的策略和基本的服务过度使用保护。

限流确定方式

与权限控制和身份验证一样,REST framework 中的限流始终定义为类列表。在视图主体运行之前,会检查列表中的每个限流策略。如果任何一个限流检查失败,将引发 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 中的速率可以指定为秒、分钟、小时或天的周期。周期必须在 / 分隔符后使用 smhd 分别指定。为了提高清晰度,还可以使用扩展单位如 secondminutehourday 或者缩写如 secminhr,因为只有第一个字符用于识别速率。

也可以在每个视图或每个视图集的基础上设置限流策略,使用基于类的视图 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 网关背后客户端将被视为一个单一客户端。

关于 X-Forwarded-For 头的工作原理和识别远程客户端 IP 的更多信息可以在此处找到。

缓存设置

REST framework 提供的限流类使用 Django 缓存后端。应确保设置了适当的缓存设置。对于简单设置,默认的 LocMemCache 后端应该可以正常使用。更多详情请参阅 Django 缓存文档。

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

from django.core.cache import caches

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

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

关于并发的注意事项

内置的限流实现可能会出现竞争条件,因此在高并发下可能会允许一些额外的请求通过。

如果你的项目需要在并发请求期间保证请求数量,则需要实现自己的限流类。

API 参考

AnonRateThrottle

AnonRateThrottle 只会限制未认证用户的请求速率。使用传入请求的 IP 地址生成唯一键来进行限流。允许的请求速率由以下方式确定(按优先级顺序):

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

AnonRateThrottle 适用于限制未知来源的请求速率。

UserRateThrottle

UserRateThrottle 会将用户限制为给定的 API 请求速率。使用用户 ID 生成唯一键来进行限流。对于未认证请求,将回退到使用传入请求的 IP 地址生成唯一键来进行限流。允许的请求速率由以下方式确定(按优先级顺序):

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

可以同时使用多个 UserRateThrottle。为此,可以通过覆盖 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

你可能感兴趣的:(djangopython)