天天看點

django中自定義限流

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
__title__ = ''
__author__ = 'sWX5327615'
__mtime__ = '2020/8/4'
"""
import logging
import time
from rest_framework.throttling import SimpleRateThrottle

logger = logging.getLogger('mylogger')

class VisitThrottleMixin(object):
    '''
    根據Django-rest-framework的限流原理改寫,根據滾動時間分片限流,應對瞬時高并發
    有一定的局限性
    作者:孫廣慶 WWX5327615
    '''
    # 60s内可以通路最大次數
    MAX_CAPACITY = 10
    # 設定逾時時間
    TIME_DELTA = 60

    def __init__(self):
        # 用于await計算剩餘通路時間
        self.history = None
        self.__visit_key = '__visit_throttle_key'

    def allow_request(self, request, view):
        mehtod = request.method
        uri_name = request.path_info
        if mehtod == "POST" and uri_name == "/task/task/":
            ctime = time.time()
            # 使用者第一次通路,将其進行記錄,并且傳回True,允許繼續通路
            if self.__visit_key not in request.session:
                request.session[self.__visit_key] = [ctime, ]
                return True
            # 如果不是第一次通路,擷取所有的記錄在
            history = request.session.get(self.__visit_key)
            self.history = history
            # 如果有曆史通路記錄,并且最早一次的通路記錄離目前時間超過60s,就删除最早的那個通路記錄,
            # 隻要為True,就一直循環超過60s就删除最早的一次通路記錄
            while history and history[-1] < (ctime - self.TIME_DELTA):
                history.pop()
            # 如果通路記錄不超過MAX_CAPACIRY次,就把目前的通路記錄插到第一個位置(pop删除最後一個)
            if len(history) < self.MAX_CAPACITY:
                history.insert(0, ctime)
                request.session[self.__visit_key] = history
                return True
            return False
        else:
            return True

    def wait(self):
        '''還需要等多久才能通路'''
        ctime = time.time()
        return self.TIME_DELTA - (ctime - self.history[-1])


VISIT_RECORD = {} #定義全局變量,用于存放通路記錄
class VisitThrottle(object):
    # 60s内可以通路最大次數
    MAX_CAPACITY = 6
    # 設定逾時時間
    TIME_DELTA = 60
    def __init__(self):

        #用于await計算剩餘通路時間
        self.history = None

    def allow_request(self,request,view):
        #擷取使用者ip作為唯一的标示
        if request.META.get('HTTP_X_FORWARDED_FOR'):
            remote_addr = request.META.get("HTTP_X_FORWARDED_FOR")
        else:
            remote_addr = request.META.get('REMOTE_ADDR')

        # 擷取目前通路的時刻
        ctime = time.time()
        # 這是使用者第一次通路,将其進行記錄,并且傳回True,允許繼續通路
        if remote_addr not in VISIT_RECORD:
            VISIT_RECORD[remote_addr] = [ctime,]
            return True

        # 如果不是第一次通路,擷取所有的記錄
        history = VISIT_RECORD.get(remote_addr)

        self.history = history
        # 判斷最開始的時刻與現在的時刻的內插補點是否在規定的時間範圍内,比如在60s内,如果不在,
        # 可以去除最開始的時刻記錄
        while history and history[-1] < ctime - self.TIME_DELTA:
            history.pop()
        # 此時清單中的時刻記錄都是在規定的時間範圍内,判斷時刻的個數也就是通路的次數

        if len(history) < self.MAX_CAPACITY:
            history.insert(0,ctime)
            return True

    def wait(self):
        # 還需要等多少秒才能通路
        ctime = time.time()
        return self.TIME_DELTA - (ctime - self.history[-1])