限流,顧名思義,就是限制對 API 的調用頻率。每一次 API 調用,都要花費服務器的資源,因此很多 API 不會對用戶無限次地開放,請求達到某個次數后就不再允許訪問了,或者一段時間內,最多只允許訪問 API 指定次數。
目前,我們的接口是沒有任何限流措施的,只要用戶調用接口,服務器就會處理並返回數據。為了防止接口被惡意用戶刷爆,我們來給接口限流。
上一篇中我們已經整理了接口並加入了緩存,我們的限流政策可以根據緩存的設置情況來制定。對於緩存時間較長的接口,可以適當放寬限制,而對於可能需要訪問數據庫的接口,則進行嚴格的限制。
django-rest-framework 為我們提供了 2 個常用的限流功能輔助類,分別是 AnonRateThrottle
和 UserRateThrottle
。AnonRateThrottle
用於限制未認證用戶的訪問頻率,限制依據是用戶的 ip。UserRateThrottle
用於限定認證用戶,即網站的注冊用戶(目前我們博客不支持用戶登錄注冊,所以這個類沒什么用)。兩個類可以用於同一 API,以便對不同類型的用戶實施不同的限流政策。
這兩個輔助類限制頻率的指定格式為 "最大訪問次數/時間間隔",例如設置為 10/min
,則只允許一分鍾內最多調用接口 10 次。超過限定次數的調用將拋出 exceptions.Throttled
異常,客戶端收到 429 狀態碼(too many requests)的響應。
再次根據已有 API 列表和緩存情況來分析一下我們的限流政策:
接口名 | URL | 限流 |
---|---|---|
文章列表 | /api/posts/ | 10/min |
文章詳情 | /api/posts/:id/ | 10/min |
分類列表 | /categories/ | 10/min |
標簽列表 | /tags/ | 10/min |
歸檔日期列表 | /posts/archive/dates/ | 10/min |
評論列表 | /api/posts/:id/comments/ | 10/min |
文章搜索結果 | /api/search/ | 5/min |
補充說明:
首頁文章列表 API:有緩存,正常用戶不會訪問太頻繁,限定 10/min
文章詳情 API:有緩存,正常用戶不會訪問太頻繁,限定 10/min
分類、標簽、歸檔日期列表,有緩存,正常用戶不會訪問太頻繁,限定 10/min
評論列表,有緩存,正常用戶不會訪問太頻繁,限定 10/min
搜索接口,正常用戶不會訪問太頻繁,限定 5/min
接口限流規則制定好后,接下來就設置限流輔助類就可以了。
啟用限流有 2 種方式,一是全局設置,二是單個視圖設置,單個視圖的設置會覆蓋全局設置。因為幾乎所有接口都是對匿名用戶限流,因此先來進行全局設置。在項目配置文件 common.py 中找到 REST_FRAMEWORK
配置項,加入如下配置:
# filename="common.py"
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.AnonRateThrottle',
],
'DEFAULT_THROTTLE_RATES': {
'anon': '10/min',
}
}
這樣,所有接口訪問頻率均被設置為 10/min。
對於搜索接口,我們制定的限流規則是 5/min,因此我們對這個視圖集的限流類進行單獨設置。
因為全局配置中,默認設置的限流頻率為 10/min,為了將限流類的默認頻率設置為 5/min,我們需要繼承原限流類覆蓋它的 THROTTLE_RATES
屬性,代碼非常簡單:
# filename="blog/views.py"
from rest_framework.throttling import AnonRateThrottle
class PostSearchAnonRateThrottle(AnonRateThrottle):
THROTTLE_RATES = {"anon": "5/min"}
接着在搜索接口的視圖集中通過 throttle_classes
指定這個限流類:
# filename="blog/views.py"
class PostSearchView(HaystackViewSet):
index_models = [Post]
serializer_class = PostHaystackSerializer
throttle_classes = [PostSearchAnonRateThrottle]
我們來測試一下,限流是否真的起了作用。
首先來測試 10/min 訪問限制的接口,以文章列表接口 api/v1/posts/ 為例,在連續訪問 10 次后,接口返回了如下結果:
HTTP 429 Too Many Requests Allow: GET, HEAD, OPTIONS Content-Type: application/json Retry-After: 52 Vary: Accept
{ "detail": "請求超過了限速。Expected available in 52 seconds." }
一分鍾后重新訪問又恢復了正常。
再來測試一下搜索接口,訪問 /api/v1/search/?text=markdown,在連續刷新 5 次后,接口返回如下結果:
HTTP 429 Too Many Requests Allow: GET, HEAD, OPTIONS Content-Type: application/json Retry-After: 26 Vary: Accept
{ "detail": "請求超過了限速。Expected available in 26 seconds." }
一分鍾后重新訪問又恢復了正常。
!!! note "注意"
因為搜索功能依賴 Elasticsearch 服務,因此測試接口時需要運行 Docker 容器,可參考《基於 drf-haystack 實現文章搜索接口》這篇文章。
https://www.zmrenwu.com/courses/django-rest-framework-tutorial/materials/101/
參考資料
[1]
HelloGitHub-追夢人物: https://www.zmrenwu.com
點個“在看”支持一下????