DRF的限流元件(原始碼分析)

2023-04-21 21:01:40

DRF限流元件(原始碼分析)

限流,限制使用者存取頻率,例如:使用者1分鐘最多存取100次 或者 簡訊驗證碼一天每天可以傳送50次, 防止盜刷。

  • 對於匿名使用者,使用使用者IP作為唯一標識。
  • 對於登入使用者,使用使用者ID或名稱作為唯一標識。
快取={
	使用者標識:[12:33,12:32,12:31,12:30,12,]    1小時/5次   12:34   11:34
{

1. 設定快取

# settings.py
CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            "PASSWORD": "qwe123",
        }
    }
}

2. 自定義限流類

# -*- encoding:utf-8 -*-
# @time: 2023/4/21 15:41
# @author: ifeng
from django.core.cache import cache as default_cache
from rest_framework import exceptions
from rest_framework import status
from rest_framework.throttling import SimpleRateThrottle


class ThrottledException(exceptions.APIException):
    status_code = status.HTTP_429_TOO_MANY_REQUESTS
    default_code = 'throttled'


class MyRateThrottle(SimpleRateThrottle):
    cache = default_cache  # 存取記錄存放在django的快取中(需設定快取)
    scope = 'user'  # 構造快取中的key
    cache_format = 'throttle_%(scope)s_%(ident)s'

    # 設定其他存取評率, 例如: 一分鐘允許存取10次
    # 其他: 's': 'sec', 'm': 'min', 'h': 'hour', 'd': 'day'
    THROTTLE_RATES = {'user': '10/m'}

    def get_cache_key(self, request, view):
        if request.user:
            ident = request.user.id
        else:
            ident = self.get_ident(request)  # 獲取請求使用者IP(request中找請求頭)

        # throttle_u # throttle_user_11.11.11.11ser_2

        return self.cache_format % {'scope': self.scope, 'ident': ident}

    def throttle_failure(self):
        wait = self.wait()
        detail = {
            'code': 1005,
            'data': '存取頻率限制',
            'detail': '需要等待 %s s才能存取' % (int(wait))
        }
        raise ThrottledException(detail)

3. 使用限流類

  • 區域性設定(views)
class UserView(APIView):
    throttle_classes = [MyRateThrottle, ]  # 限流
  • 全域性設定(settings)
REST_FRAMEWORK = {
    # 限流
    "DEFAULT_THROTTLE_CLASSES": ["app01.throttle.MyRateThrottle", ],
    "DEFAULT_THROTTLE_RATES": {
        "user": "10/m",
        # "xx":"100/h"
    }
}

4. 多個限流類

本質,每個限流的類中都有一個 allow_request 方法,此方法內部可以有三種情況:

  • 返回True,表示當前限流類允許存取,繼續執行後續的限流類。
  • 返回False,表示當前限流類不允許存取,繼續執行後續的限流類。所有的限流類執行完畢後,讀取所有不允許的限流,並計算還需等待的時間。
  • 丟擲異常,表示當前限流類不允許存取,後續限流類不再執行。

5. 原始碼分析

  1. 這是限流大體的執行邏輯, 後面將對allow_reqeust中具體分析

  1. allow_request()在自定義的類裡面沒定義, 所以我們到父類別SimpleRateThrottle執行allow_request()方法