限流,顧名思義,就是限制對 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 實現文章搜尋介面。
關注公眾號加入我們