第 14 篇:限制介面的訪問頻率

削微寒發表於2020-07-31

作者:HelloGitHub-追夢人物

限流,顧名思義,就是限制對 API 的呼叫頻率。每一次 API 呼叫,都要花費伺服器的資源,因此很多 API 不會對使用者無限次地開放,請求達到某個次數後就不再允許訪問了,或者一段時間內,最多隻允許訪問 API 指定次數。

目前,我們的介面是沒有任何限流措施的,只要使用者呼叫介面,伺服器就會處理並返回資料。為了防止介面被惡意使用者刷爆,我們來給介面限流。

上一篇中我們已經整理了介面並加入了快取,我們的限流政策可以根據快取的設定情況來制定。對於快取時間較長的介面,可以適當放寬限制,而對於可能需要訪問資料庫的介面,則進行嚴格的限制。

django-rest-framework 為我們提供了 2 個常用的限流功能輔助類,分別是 AnonRateThrottleUserRateThrottleAnonRateThrottle 用於限制未認證使用者的訪問頻率,限制依據是使用者的 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

補充說明:

  1. 首頁文章列表 API:有快取,正常使用者不會訪問太頻繁,限定 10/min
  2. 文章詳情 API:有快取,正常使用者不會訪問太頻繁,限定 10/min
  3. 分類、標籤、歸檔日期列表,有快取,正常使用者不會訪問太頻繁,限定 10/min
  4. 評論列表,有快取,正常使用者不會訪問太頻繁,限定 10/min
  5. 搜尋介面,正常使用者不會訪問太頻繁,限定 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 實現文章搜尋介面


關注公眾號加入我們

相關文章