Django Rest Framework 源碼解析--節流

Django Rest Framework 源碼解析--節流

上一篇博客,restframework重寫的dispatch()方法中,執行了inital()函數。inital()中check_throttles((request) 方法實現了請求的訪問頻率控制功能。

1、check_throttles(request)函數中,循環了限流類的對象列表,依次執行限流對象的 allow_request() 方法

def check_throttles(self, request):
    """
    Check if request should be throttled.
    Raises an appropriate exception if the request is throttled.
    
    檢查請求是否應該被節流。
    如果被節流,則拋出相應異常。
    """
    # 遍歷 限流對象列表,如果返回Fasle,則被限流,拋出異常(可傳參數throttle.wait()的返回值)
    for throttle in self.get_throttles():
        # 如果被節流,返回False,則拋出相應異常
        if not throttle.allow_request(request, self):
            self.throttled(request, throttle.wait())

2、self.get_throttles()限流對象列表生成式

def get_throttles(self):
    """
    Instantiates and returns the list of throttles that this view uses.
    """
    # 可以在view中重寫throttle_classes,指定限流對象列表
    # 也可以在setting.py中定義
    return [throttle() for throttle in self.throttle_classes]

3、如果被節流,則通過throttled()方法,拋出相應異常,可傳入wait作爲等待時間的參數

def throttled(self, request, wait):
    """
    If request is throttled, determine what kind of exception to raise.
    """
    # wait參數,出入的值是 節流類的wait()方法的返回值(單位:秒)
    raise exceptions.Throttled(wait)

使用示例:

1、自定義限流類,繼承BaseThrottle類,重寫 allow_request()wait() 這兩個方法

from rest_framework.throttling import BaseThrottle

import time

# 保存訪問記錄
VISIT_RECORD = {}

class VisitThrottle(BaseThrottle):
    """
    自定義限流類:60秒內只能訪問3次,超過就限流
    
    返回True,允許請求訪問
    返回False,禁止請求訪問
    """
    
    
    # 通過 self.history這個對象的成員變量,
    # 在allow_request()和 wait()這兩個成員方法之間傳遞history的值
    def __init__(self):
        self.history = None  # 初始化訪問記錄

    def allow_request(self, request, view):
        # 獲取用戶ip  
        remote_addr = self.get_ident(request)
        timer = time.time()
        if remote_addr not in REQ_RECORD:
            VISIT_RECORD[remote_addr]=[timer]
            return True
        # 獲取當前ip的歷史訪問記錄
        history = VISIT_RECORD[remote_addr]
        self.history = history
        # 如果有歷史訪問記錄,並且最早一次的訪問記錄離當前時間超過60s,就刪除最早的那個訪問記錄,
        # 只要爲True,就一直循環刪除最早的一次訪問記錄
        while history and history[-1] < timer - 60:
            history.pop()

        # 60秒內的訪問記錄,是否超過3次
        # 如果沒有超過,則記錄這次訪問,並返回True,允許訪問
        # 如果超過,則返回False,禁止訪問
        if len(history) < 3:
            history.insert(0, timer)
            return True
        return False

    def wait(self):
        '''還需要等多久才能訪問'''
        timer = time.time()
        return 60 - (timer - self.history[-1])

2、在View中調用限流類

class TestView(APIView):
    # 在View中重寫throttle_classes限流類列表,一般只寫一個限流,
    # 或者不限流,使列表爲空,throttle_classes = []
    throttle_classes = [VisitThrottle, ]

    def get(self,request,*args,**kwargs):
        pass

3、或者在setting.py中指定全站默認使用的限流類的路徑

REST_FRAMEWORK = {
    "DEFAULT_THROTTLE_CLASSES": ('plugin.restframework.throttling.VisitThrottle')
}

內置的限流類

與認證類、鑑權類通常繼承BaseXXX類,高度自定義不同,限流類我個人覺得繼承restframework提供的其他內置的類更方便

例如繼承 SimpleRateThrottle

class TestTrottle(SimpleRateThrottle):
    # 設定規定時間內能訪問的次數,例如 3/m, 1/s, 1000/h, 9999/day
    # 通常設定在setting.py中
    THROTTLE_RATES = {
        "Test": '5/m'
    }
    # 指定scope值爲 查找THROTTLE_RATES的key
    scope = "Test"
    
    def get_cache_key(self, request, view):
        # 通過ip限制節流
        return self.get_ident(request)
        
        # return request.user.pk   # 通常也使用requser.user作爲標識一個用戶的ID

 

發佈了74 篇原創文章 · 獲贊 15 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章