頻率組件api
-使用:
-第一步,寫一個頻率類,繼承SimpleRateThrottle
#重寫get_cache_key,返回self.get_ident(request)
#必定要記住配置一個scop=字符串ide
class Throttle(SimpleRateThrottle): scope = 'lxx' def get_cache_key(self, request, view): return self.get_ident(request)
-第二步:在setting中配置
ui
REST_FRAMEWORK = { 'DEFAULT_THROTTLE_RATES':{ 'lxx':'3/m' }}
-局部使用
-在視圖類中配置:
-throttle_classes=[Throttle,]
-全局使用
-在setting中配置
'DEFAULT_THROTTLE_CLASSES':['本身定義的頻率類'],
-局部禁用
throttle_classes=[]
頻率組件分析:
-核心源代碼:spa
def check_throttles(self, request): for throttle in self.get_throttles(): if not throttle.allow_request(request, self): self.throttled(request, throttle.wait())
-自定義的頻率類:詳解代碼views.py 25行開始code
#自定義頻率類 class MyThrottle(BaseThrottle): VISIT_RECORD = {} def __init__(self): self.history=None def allow_request(self,request,view): # 校驗是否能夠訪問 #自定義控制每分鐘訪問多少次,運行訪問返回true,不容許訪問返回false # (1)取出訪問者ip{ip1:[第二次訪問時間,第一次訪問時間],ip2:[]} # (2)判斷當前ip不在訪問字典裏,若是不在添加進去,而且直接返回True,表示第一次訪問,在字典裏,繼續往下走 # (3)循環判斷當前ip的列表,有值,而且當前時間減去列表的最後一個時間大於60s,把這種數據pop掉,這樣列表中只有60s之內的訪問時間, # (4)判斷,當列表小於3,說明一分鐘之內訪問不足三次,把當前時間插入到列表第一個位置,返回True,順利經過 # (5)當大於等於3,說明一分鐘內訪問超過三次,返回False驗證失敗 # (1)取出訪問者ip # print(request.META) #取出訪問者ip ip = request.META.get('REMOTE_ADDR') import time #拿到當前時間 ctime = time.time() # (2)判斷當前ip不在訪問字典裏,添加進去,而且直接返回True,表示第一次訪問 if ip not in self.VISIT_RECORD: self.VISIT_RECORD[ip] = [ctime, ] return True #是個當前訪問者ip對應的時間列表 [第一次訪問的時間,] self.history = self.VISIT_RECORD.get(ip) # (3)循環判斷當前ip的列表,有值,而且當前時間減去列表的最後一個時間大於60s,把這種數據pop掉,這樣列表中只有60s之內的訪問時間, while self.history and ctime - self.history[-1] > 60: self.history.pop() # (4)判斷,當列表小於3,說明一分鐘之內訪問不足三次,把當前時間插入到列表第一個位置,返回True,順利經過 # (5)當大於等於3,說明一分鐘內訪問超過三次,返回False驗證失敗 if len(self.history) < 3: self.history.insert(0, ctime) return True else: return False def wait(self): import time ctime = time.time()
# 返回多少秒以後能夠再次訪問 return 60 - (ctime - self.history[-1]) class Books(APIView): # throttle_classes=[Throttle,] throttle_classes=[MyThrottle,] def get(self,request): return Response('')
-SimpleRateThrottle源碼orm
class SimpleRateThrottle(BaseThrottle): """ A simple cache implementation, that only requires `.get_cache_key()` to be overridden. The rate (requests / seconds) is set by a `rate` attribute on the View class. The attribute is a string of the form 'number_of_requests/period'. Period should be one of: ('s', 'sec', 'm', 'min', 'h', 'hour', 'd', 'day') Previous request information used for throttling is stored in the cache. """ cache = default_cache timer = time.time cache_format = 'throttle_%(scope)s_%(ident)s' scope = None THROTTLE_RATES = api_settings.DEFAULT_THROTTLE_RATES def __init__(self): if not getattr(self, 'rate', None): self.rate = self.get_rate() self.num_requests, self.duration = self.parse_rate(self.rate) def get_cache_key(self, request, view): """ Should return a unique cache-key which can be used for throttling. Must be overridden. May return `None` if the request should not be throttled. """ raise NotImplementedError('.get_cache_key() must be overridden') def get_rate(self): """ Determine the string representation of the allowed request rate. """ if not getattr(self, 'scope', None): msg = ("You must set either `.scope` or `.rate` for '%s' throttle" % self.__class__.__name__) raise ImproperlyConfigured(msg) try: return self.THROTTLE_RATES[self.scope] except KeyError: msg = "No default throttle rate set for '%s' scope" % self.scope raise ImproperlyConfigured(msg) def parse_rate(self, rate): """ Given the request rate string, return a two tuple of: <allowed number of requests>, <period of time in seconds> """ if rate is None: return (None, None) num, period = rate.split('/') num_requests = int(num) duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]] return (num_requests, duration) def allow_request(self, request, view): """ Implement the check to see if the request should be throttled. On success calls `throttle_success`. On failure calls `throttle_failure`. """ if self.rate is None: return True self.key = self.get_cache_key(request, view) if self.key is None: return True self.history = self.cache.get(self.key, []) self.now = self.timer() # Drop any requests from the history which have now passed the # throttle duration while self.history and self.history[-1] <= self.now - self.duration: self.history.pop() if len(self.history) >= self.num_requests: return self.throttle_failure() return self.throttle_success() def throttle_success(self): """ Inserts the current request's timestamp along with the key into the cache. """ self.history.insert(0, self.now) self.cache.set(self.key, self.history, self.duration) return True def throttle_failure(self): """ Called when a request to the API has failed due to throttling. """ return False def wait(self): """ Returns the recommended next request time in seconds. """ if self.history: remaining_duration = self.duration - (self.now - self.history[-1]) else: remaining_duration = self.duration available_requests = self.num_requests - len(self.history) + 1 if available_requests <= 0: return None return remaining_duration / float(available_requests)