Дросселирование

Дросселирование

HTTP/1.1 420 Повышение спокойствия

Twitter API ограничение скорости ответа

Дросселирование аналогично permissions, поскольку оно определяет, должен ли запрос быть авторизован. Дроссели обозначают временное состояние и используются для контроля скорости запросов, которые клиенты могут делать к API.

Как и в случае с разрешениями, можно использовать несколько дросселей. Ваш API может иметь ограничительный дроссель для неаутентифицированных запросов и менее ограничительный дроссель для аутентифицированных запросов.

Еще один сценарий, в котором вам может понадобиться использовать несколько дросселей, - это если вам нужно наложить различные ограничения на разные части API, поскольку некоторые сервисы являются особенно ресурсоемкими.

Несколько дросселей также можно использовать, если вы хотите наложить дросселирование как на скорость разрыва, так и на скорость устойчивого дросселирования. Например, вы можете ограничить пользователя максимум 60 запросами в минуту и 1000 запросами в день.

Дроссели не обязательно относятся только к запросам на ограничение скорости. Например, служба хранения данных может также нуждаться в ограничении пропускной способности, а платная служба данных может захотеть ограничить доступ к определенному количеству записей.

**Дросселирование на уровне приложения, которое обеспечивает DRF, не следует рассматривать как меру безопасности или защиту от перебора или атак типа "отказ в обслуживании". Намеренно злоумышленники всегда смогут подделать IP-адреса. В дополнение к этому, встроенная реализация дросселирования реализована с использованием кэш-фреймворка Django и использует неатомарные операции для определения скорости запросов, что иногда может привести к некоторой нечеткости.

Дросселирование на уровне приложений, предоставляемое DRF, предназначено для реализации таких политик, как различные бизнес-уровни и базовая защита от чрезмерного использования услуг**.

Как определяется дросселирование

Как и в случае с разрешениями и аутентификацией, дросселирование в DRF всегда определяется как список классов.

Перед запуском основной части представления проверяется каждый дроссель в списке. Если какая-либо проверка дросселя не прошла, будет вызвано исключение exceptions.Throttled, и основное тело представления не будет запущено.

Установка политики дросселирования

Политика дросселирования по умолчанию может быть установлена глобально, с помощью параметров DEFAULT_THROTTLE_CLASSES и DEFAULT_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, могут включать second, minute, hour или day в качестве периода дросселирования.

Вы также можете установить политику дросселирования на основе каждого представления или каждого набора представлений, используя представления на основе класса 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)

Как определяются клиенты

HTTP-заголовок X-Forwarded-For и WSGI-переменная REMOTE_ADDR используются для уникальной идентификации IP-адресов клиентов для дросселирования. Если заголовок X-Forwarded-For присутствует, то он будет использоваться, иначе будет использоваться значение переменной REMOTE_ADDR из среды WSGI.

Если вам необходимо строго идентифицировать уникальные IP-адреса клиентов, вам нужно сначала настроить количество прокси-серверов приложений, за которыми работает API, установив параметр NUM_PROXIES. Это значение должно быть целым числом, равным нулю или больше. Если значение ненулевое, то IP-адрес клиента будет идентифицироваться как последний IP-адрес в заголовке X-Forwarded-For, после того как IP-адреса прокси приложений будут исключены. Если установлено в ноль, то значение REMOTE_ADDR всегда будет использоваться в качестве идентифицирующего IP-адреса.

Важно понимать, что если вы настроите параметр NUM_PROXIES, то все клиенты за уникальным NAT-ом шлюзом будут рассматриваться как один клиент.

Дополнительную информацию о том, как работает заголовок X-Forwarded-For и как определить IP удаленного клиента, можно найти здесь.

Настройка кэша

Классы дросселирования, предоставляемые DRF, используют бэкенд кэша Django. Вы должны убедиться, что установили соответствующие настройки cache settings. Значение по умолчанию бэкенда LocMemCache должно быть приемлемым для простых настроек. Более подробную информацию можно найти в документации по кэшу Django.

Если вам нужно использовать кэш, отличный от 'default', вы можете сделать это, создав пользовательский класс дросселя и установив атрибут cache. Например:

from django.core.cache import caches

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

Вам нужно будет не забыть также установить ваш пользовательский класс дросселя в ключе настроек 'DEFAULT_THROTTLE_CLASSES' или с помощью атрибута представления throttle_classes.

Заметка о параллелизме

Встроенные реализации дросселей открыты для условий гонки, поэтому при высоком параллелизме они могут пропустить несколько лишних запросов.

Если ваш проект полагается на гарантию количества запросов во время одновременных запросов, вам необходимо реализовать свой собственный класс дросселя. Более подробную информацию смотрите в issue #5181.


API Reference

AnonRateThrottle

Дроссель AnonRateThrottle будет дросселировать только неаутентифицированных пользователей. IP-адрес входящего запроса используется для генерации уникального ключа для дросселирования.

Допустимая скорость запроса определяется по одному из следующих параметров (в порядке предпочтения).

  • Свойство rate класса, которое может быть предоставлено путем переопределения AnonRateThrottle и установки свойства.

  • Настройка DEFAULT_THROTTLE_RATES['anon'].

AnonRateThrottle подходит, если вы хотите ограничить скорость запросов от неизвестных источников.

UserRateThrottle

Дроссель UserRateThrottle будет дросселировать пользователей до заданной скорости запросов через API. Идентификатор пользователя используется для генерации уникального ключа для дросселирования. Неаутентифицированные запросы будут возвращаться к использованию IP-адреса входящего запроса для генерации уникального ключа для дросселирования.

Допустимая скорость запроса определяется по одному из следующих параметров (в порядке предпочтения).

  • Свойство rate класса, которое может быть предоставлено путем переопределения UserRateThrottle и установки свойства.

  • Настройка DEFAULT_THROTTLE_RATES['user'].

API может иметь несколько UserRateThrottles одновременно. Для этого переопределите UserRateThrottle и установите уникальный 'scope' для каждого класса.

Например, несколько пользовательских дросселей могут быть реализованы с помощью следующих классов...

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. Уникальный ключ дросселя будет сформирован путем соединения 'scope' запроса с уникальным идентификатором пользователя или IP-адресом.

Допустимая скорость запроса определяется настройкой DEFAULT_THROTTLE_RATES, используя ключ из 'scope' запроса.

Например, учитывая следующие представления...

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'
    }
}

Запросы пользователей к ContactListView или ContactDetailView будут ограничены до 1000 запросов в день. Запросы пользователей к UploadView будут ограничены 20 запросами в день.


Пользовательские дроссели

Чтобы создать пользовательский дроссель, переопределите BaseThrottle и реализуйте .allow_request(self, request, view). Метод должен возвращать True, если запрос должен быть разрешен, и False в противном случае.

По желанию вы также можете переопределить метод .wait(). Если он реализован, .wait() должен возвращать рекомендуемое количество секунд ожидания перед попыткой следующего запроса или None. Метод .wait() будет вызван только в том случае, если .allow_request() ранее вернул False.

Если реализован метод .wait() и запрос дросселируется, то в ответ будет включен заголовок Retry-After.

Пример

Ниже приведен пример дросселирования скорости, которое будет случайным образом дросселировать 1 из каждых 10 запросов.

import random

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

Last updated