throttling.py

スロットリング

HTTP/1.1 420 Enhance Your Calm

Twitter APIレート制限レスポンス

スロットリングはパーミッションと同様に、リクエストを承認する必要があるかどうかを決定します。スロットルは一時的な状態を示し、クライアントがAPIに対して行うリクエストの速度を制御するために使用されます。

パーミッションと同様に、複数のスロットルを使用できます。認証されていないリクエストには制限の厳しいスロットルを、認証されたリクエストには制限の緩いスロットルを使用できます。

複数のスロットルを使用する必要があるもう1つのシナリオは、一部のサービスが特にリソースを消費するため、APIの異なる部分に異なる制約を課す必要がある場合です。

バーストスロットリングレートと持続スロットリングレートの両方を課したい場合にも、複数のスロットルを使用できます。たとえば、ユーザーを1分あたり最大60リクエスト、1日あたり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で使用されるレートの説明には、スロットル期間として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が実行されているアプリケーションプロキシの数を構成する必要があります。この設定は0以上の整数である必要があります。0以外の値に設定されている場合、クライアントのIPは、アプリケーションプロキシのIPアドレスが除外された後、X-Forwarded-Forヘッダーの最後のIPアドレスとして識別されます。0に設定されている場合、REMOTE_ADDRの値は常に識別IPアドレスとして使用されます。

NUM_PROXIES設定を構成した場合、一意のNAT'dゲートウェイの背後にあるすべてのクライアントは、単一のクライアントとして扱われることに注意することが重要です。

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ビュー属性を使用して、カスタムスロットルクラスを設定することを忘れないでください。

同時実行性に関する注意

組み込みのスロットリング実装は競合状態の影響を受けやすいため、高同時実行性下では追加のリクエストが許可される場合があります。

プロジェクトが同時リクエスト中のリクエスト数を保証することに依存している場合は、独自のthrottleクラスを実装する必要があります。詳細についてはissue #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'
    }
}

ContactListViewまたはContactDetailViewへのユーザーリクエストは、1日あたり合計1000リクエストに制限されます。UploadViewへのユーザーリクエストは、1日あたり20リクエストに制限されます。


カスタムスロットル

カスタムスロットルを作成するには、BaseThrottleをオーバーライドして.allow_request(self, request, view)を実装します。このメソッドは、リクエストを許可する場合はTrueを、そうでない場合はFalseを返す必要があります。

必要に応じて、.wait()メソッドをオーバーライドすることもできます。実装されている場合、.wait()は、次のリクエストを試行する前に待機することをお勧めする秒数を返すか、Noneを返す必要があります。.wait()メソッドは、.allow_request()が以前にFalseを返した場合にのみ呼び出されます。

.wait()メソッドが実装されていてリクエストがスロットリングされている場合、Retry-Afterヘッダーがレスポンスに含まれます。

これは、10リクエストごとにランダムに1リクエストをスロットリングするレートスロットルの例です。

import random

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