Django REST framework API 指南(15):限流

wcode發表於2018-03-18

官方原文連結
本系列文章 github 地址
轉載請註明出處

限流(Throttling)

限流與許可權類似,因為它確定是否應該授權請求。 限流閥指示臨時狀態,並用於控制客戶端可以對API進行的請求速率。

與許可權一樣,可能會使用多種限流方式。你的 API 可能對未經身份驗證的請求進行限流,對經過身份驗證的請求限流較少。

如果你需要對 API 的不同部分使用不同的限流策略,由於某些服務特別佔用資源,你可能想要使用同時有多種限流策略的另一種方案。

如果你想要同時實現爆發限流率和持續限流率,也可以使用多個限流閥。例如,你可能希望將使用者限制為每分鐘最多 60 個請求,並且每天最多 1000 個請求。

限流閥不一定只限制請求頻率。例如,儲存服務可能還需要對頻寬進行限制,而付費資料服務可能希望對正在訪問的某些記錄進行限制。

如何確定限流

與許可權和身份驗證一樣,REST framework 中的限流始終定義為類的列表。

在執行檢視的主體之前,會檢查列表中的每個限流閥。如果任何限流檢查失敗,將引發一個 exceptions.Throttled 異常,並且該檢視的主體將不會再執行。

設定限流策略

可以使用 DEFAULT_THROTTLE_CLASSESDEFAULT_THROTTLE_RATES setting 全域性設定預設限流策略。例如:

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)
複製程式碼

如何識別客戶端

X-Forwarded-For HTTP header 和 REMOTE_ADDR WSGI 變數用於唯一標識用於限流的客戶端 IP 地址。如果存在 X-Forwarded-For header ,則會使用它,否則將使用 WSGI 環境中的 REMOTE_ADDR 變數的值。

如果你需要嚴格標識唯一的客戶端 IP 地址,則需要先通過設定 NUM_PROXIES setting 來配置 API 執行的應用代理的數量。該設定應該是一個零或更大的整數。如果設定為非零,則一旦任何應用程式代理 IP 地址首先被排除,客戶端 IP 將被標識為 X-Forwarded-For header 中的最後一個 IP 地址。如果設定為零,則 REMOTE_ADDR 值將始終用作識別 IP 地址。

重要的是要理解,如果你配置了 num_proxy 設定,那麼在一個唯一的 NAT 的閘道器後面的所有客戶端將被當作一個單獨的客戶機來對待。

關於 X-Forwarded-For header 如何工作以及識別遠端客戶端 IP 的更多內容可以在這裡找到

設定快取

REST framework 提供的限流類使用 Django 的快取後端。你應該確保你已經設定了適當的快取 setting 。對於簡單的設定,LocMemCache 後端的預設值應該沒問題。有關更多詳細資訊,請參閱 Django 的快取文件

如果你需要使用 'default' 以外的快取,則可以通過建立自定義限流類並設定 cache 屬性來實現。例如:

class CustomAnonRateThrottle(AnonRateThrottle):
    cache = get_cache('alternate')
複製程式碼

您需要記住還要在 'DEFAULT_THROTTLE_CLASSES' settings key 中設定自定義的限流類,或者使用 throttle_classes 檢視屬性。


API 參考

AnonRateThrottle

AnonRateThrottle 將永遠限制未認證的使用者。通過傳入請求的 IP 地址生成一個唯一的金鑰來進行限制。

允許的請求頻率由以下之一決定(按優先順序)。

  • 類的 rate 屬性,可以通過繼承 AnonRateThrottle 並設定屬性來提供。
  • DEFAULT_THROTTLE_RATES['anon'] 設定.

如果你想限制未知來源的請求頻率,AnonRateThrottle 是合適的。

UserRateThrottle

UserRateThrottle 通過 API 將使用者請求限制為給定的請求頻率。使用者標識用於生成一個唯一的金鑰來加以限制。未經身份驗證的請求將回退到使用傳入請求的 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 屬性時才會應用此限制。然後通過將請求的 “範圍” 與唯一的使用者標識或 IP 地址連線起來形成唯一的限流金鑰。

允許的請求頻率由 DEFAULT_THROTTLE_RATES setting 使用請求 “範圍” 中的一個鍵確定。

例如,給出以下檢視...

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 header 將包含在響應中。

舉個例子

以下是限流的一個示例,隨機地控制每 10 次請求中的 1 次。

import random

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

相關文章