45.限流Throttling及源碼解析

什么是限流?

  1. 限流類似于權限機制,它也決定是否接受當前請求,用于控制客戶端在某段時間內允許向API發出請求的次數 , 也就是頻率
  2. 假設有客戶端(比如爬蟲程序)短時間發起大量請求,超過了服務器能夠處理的能力,將會影響其他用戶的正常使用
  3. 為了保證服務的穩定性,并防止接口受到惡意用戶的攻擊 , 我們可以對接口進行限流
  4. 又或者可以對未經身份驗證的請求設置訪問頻率,對經過身份驗證的請求不限制訪問頻率
  5. 限流也不止單指限制訪問次數的措施,例如付費數據服務的特點訪問次數
限流的應用場景
  1. 區分用戶場景,比如匿名和已登錄,不同權限的用戶不同的限流策略
  2. API的不同,根據不同API設置不同的策略
  3. 請求的爆發期和持續期不同的限流策略
  4. 可以同時支持使用多個限流策略
  限流的機制限流和權限一樣,執行視圖前會依次檢查所有的限流類,全部通過會執行View,任何一個檢查失敗,會拋出Exceptions.Throttled異常在settings中,通過 DEFAULT_THROTTLE_CLASSES 設置限流類,通過DEFAULT_THROTTLE_RATES設置限流頻率  DRF提供的兩個常用限流類AnonRateThrottle:對于匿名用戶的限流,使用anon設置頻率UserRateThrottle:對于登錄用戶的限流, 使用user設置頻率 全局限流類配置REST_FRAMEWORK = {# 全局限流類的配置'DEFAULT_THROTTLE_CLASSES': ('rest_framework.throttling.AnonRateThrottle',# 對于匿名用戶的限流'rest_framework.throttling.UserRateThrottle' #對于登錄用戶的限流),# 限流頻率的配置'DEFAULT_THROTTLE_RATES': {'anon': '100/day', # 未認證用戶一天只許訪問100次'user': '1000/day' # 認證用戶一天可以訪問1000次}}DEFAULT_THROTTLE_RATES設置限流頻率格式 次數/時間單位
  • second: 按秒設置頻率次數
  • minute:按分鐘設置頻率次數
  • hour:按小時設置頻率次數
  • day: 按天設置頻率次數
 視圖級別限流類配置#導入限流模塊from rest_framework import throttling class getInfoList(ModelViewSet):# 通過throttle_classes 設置該視圖的限流類# 視圖指定會覆蓋settings設置的全局限流throttle_classes = (throttling.UserRateThrottle,)def infoList(self):...  識別請求的客戶端我們既然要對請求進行限流,那么肯定要失識 別是誰發來的請求,然后進行對應的措施,不然無法確定請求者身份,那么就無法得知是不是需要限制的請求,常見的方法有三種
  1. drf利用http報頭的 x-forwarded-for 或者wsgi中的remote-addr變量來唯一標識客戶端的IP地址
  2. 如果 存在x-forwarded-for 屬性,則使用x-forwarded-for ,否則使用remote-addr
  3. 可以使用request.user的屬性來標識請求,比如使用request.user.id 來標記唯一請求
  4. 使用IP地址對客戶端請求進行限流,需要考慮使用偽造代理IP請求的情況
  throttling源碼解析throttling源碼一共有五個類
  1. BaseThrottle: 限流基類
  2. SimpleRateThrottle:頻率校驗類
  3. AnonRateThrottle:匿名用戶限流
  4. UserRateThrottle:認證用戶限流
  5. ScopedRateThrottle:api視圖級別的限流
 BaseThrottle限流基類沒有去具體實現某些功能,跟權限類基類似,只是提供了占位方法class BaseThrottle:# allow_request源碼并沒有直接實現功能,只是寫好了方法占位,待后續繼承實現# 該方法主要是處理是否允許請求通過# 如果后續繼承基類實現該方法,允許請求通過返回True,不允許請求通過返回Falsedef allow_request(self, request, view):raise NotImplementedError('.allow_request() must be overridden')# 獲取IP地址def get_ident(self, request):# 獲取請求頭中真實IP地址xff = request.META.get('HTTP_X_FORWARDED_FOR')# 獲取代理IP地址remote_addr = request.META.get('REMOTE_ADDR')# 獲取設置的允許的最大代理數,默認不設置為Nonenum_proxies = api_settings.NUM_PROXIES# 如果num_proxies不是None,說明設置了該值if num_proxies is not None:# 如果設置為0,或者 xff沒有值if num_proxies == 0 or xff is None:# 返回代理IP地址return remote_addr#使用代理IP的話會有多個地址,使用逗號分割成一個listaddrs = xff.split(',')'''通過min函數,拿到允許的代理數和IP地址長度最小的值,使用-變成負數在addrs列表中通過該下標取對應值'''client_addr = addrs[-min(num_proxies, len(addrs))]return client_addr.strip()# 如果沒有設置允許的代理數 并且xff有值則直接返回,否則返回remote_addrreturn ''.join(xff.split()) if xff else remote_addr# 等待時間,告訴客戶端被限流,等待多久可以訪問# 后續繼承實現,可選def wait(self):return NoneSimpleRateThrottle頻率控制類,繼承了BaseThrottle,添加和重寫了一些方法,重點是添加了get_cache_key 方法,但必須自己實現該方法

推薦閱讀