預案三板斧之限流大法

原文鏈接:https://www.infoq.cn/article/L1FThcLIgzHSYlIaDk0R

作者:焦振清

限流策略:多維防禦 + 縱深防禦

限流能力

限流是針對請求的各種特徵,多維防禦 + 縱深防禦,從而限制流量,實現對服務端資源的合理使用。這裏的特徵是指一個請求所包含的各種信息,包括但不限於 IP、Header、URI、Cookie 等。常見的限流策略有以下三種(以 Nginx 爲例進行說明):

Nginx 還提供 work_connections 和 worker_processes 這類針對程序的全局設置,但是這些配置並非是客戶端的請求的特徵,不能直接作用於特定請求特徵的流量,因此不屬於本文限流的討論範疇。

多維防禦

所謂的多維防禦,不僅僅針對 IP 進行限制,還可以從 URI、Cookie、狀態碼等多個維度進行限制,同時還可以組合起來進行精細化的限制,舉例說明:

  • 狀態碼 +IP,如果惡意掃描整個網站,必然會出現較多的 404 狀態碼,這時候結合其他維度,就可以進行一定的防禦和限制

  • URI+Cookie,可以避免公司 IP 出口導致的請求被誤傷,同時也解決了 IP 模式下閾值設置太大的問題

縱深防禦

所謂的縱深防禦,則是指針對單一維度,進行多層級的防禦,實現精細化的限制。以 IP 維度爲例進行說明:

  • 每秒鐘,單個 IP 最多可以訪問 100 次

  • 每十秒鐘,單個 IP 最多可以訪問 500 次

  • 每分鐘,單個 IP 最多可以訪問 1000 次

閾值設置

上述的閾值只是簡單舉例,並沒有太大的參考價值,在實際部署的過程中,一般需要至少 30 天的訪問日誌以及多個節假日的訪問日誌進行離線分析,從而得出業務特定的合理閾值,然後在通過灰度逐步全量。初期的閾值,建議設置的比預估閾值至少高一倍,先具備限流功能,然後再慢慢打磨閾值,避免因爲閾值設置不合理,導致限流功能上線受阻,那就得不償失了。

限流的算法

那各種限流策略背後的實現機制是怎樣的呢?常見的限流算法有固定窗口計數器,滑動窗口計數器,漏洞和令牌桶四種,在各類軟件的具體實現上卻不盡相同,詳細內容可以參考這篇文章《分佈式系統關注點——限流該怎麼做?》,本文僅介紹較爲常用的兩種算法。

固定窗口計數器

將時間劃分爲固定時間窗口(例如每秒一個單位),在每個固定的時間窗口內對請求進行計數,如果請求的次數超過了限制,則本時間窗口內所有的後續請求都被丟棄,直到下一個固定時間窗口時,重置計數器。

預案三板斧的限流大法

該算法常被吐槽流量突增,舉例來說 100r/m(每分鐘 100 次請求),可能在第一秒鐘就請求完畢,更有甚者,在兩個時間窗口交界處可能會產生 2 倍的請求,這樣就會導致短時的流量突增。但是,只要把時間粒度控制在秒級,效果還是可以的。因此,滑動窗口就沒有太大的必要進行介紹了,大家看相關文章即可。

預案三板斧的限流大法

漏桶

漏桶算法,有一個固定大小的隊列,進入的請求全部放入該隊列中,如果隊列滿,則丟棄進入的請求。同時,以固定的速率從隊列中消費請求。好處就是,不管流量進來多少,出去的速率始終固定。

該算法的主要不足就是消費速率設置會偏保守一些,因爲不同請求的資源消耗和處理耗時不同,爲了能夠適配各類場景,必然會導致消費速率設置較爲保守。緩解的方法,對不同的請求類型進行集羣拆分,從而能夠更加精細化的設置消費速率。

預案三板斧的限流大法

各層級限流

全局統一接入:GFE/BFE/JFE

全局統一接入的產品,Goolge 叫做 GFE,Baidu 叫做 BFE,JD 叫做 JFE。這類產品會將公司所有對外流量進行統一的接入和調度,同時具備較強的通用防護能力,如抗 D、WAF 等。

對於一些業務定製化的複雜限流規則,一般會下放到各個業務線自己的接入端來實現。

預案三板斧的限流大法

Webserver:Nginx/OpenResty

上面介紹各種 XFE 的時候提到,一些業務定製化的複雜限流規則,會下放到各個業務線的接入端來實現,在這個階段,因爲 Webserver 的選擇較多,所以實現方式也較多,沒有統一的標準,一般是直接利用 webserver 提供的限流能力,或者是通過插件方式來實現,前者多以 Nginx 爲主,後者則是 Nginx+lua/Nginx+lua+redis 的形式。

本文以 Nginx 爲例來進行說明,業務側的整體思路應該是多維防禦 + 縱深防禦,從而實現較好的限流效果。

多維防禦,以 Nginx 的 limit_req 爲例進行說明,不僅僅對來源 IP 進行流控,還可以對請求的 uri,cookie 進行流控,還可以對 ip+uri+cookie 的組合進行流控,從多個維度下手,進而實現更好的效果。

limit_req_zone $ip zone=1:10m rate=100r/s;

limit_req_zone uriuriip zone=2:10m rate=100r/s;

limit_req_zone uriuriip$cookie zone=3:10m rate=100r/s;

limit_req zone=1 burst=30 nodelay;

limit_req zone=2 burst=10 nodelay;

limit_req zone=3 burst=30 nodelay;

縱深防禦,以 Nginx 的 limit_req 爲例進行說明,主要是通過漏斗思路,設置三級防禦體系,第一級是粗濾,因此閾值可以設置的較大,僅攔截明顯的惡意行爲即可,第二級過濾,可以攔截一些異常請求,第三級過濾,則可以將大部分異常請求進行攔截。

limit_req_zone $ip zone=11:10m rate=100r/s;

limit_req_zone $ip zone=12:10m rate=500r/10s;

limit_req_zone $ip zone=13:10m rate=1200r/1m;

limit_req zone=1 burst=50 nodelay;

limit_req zone=2 burst=100 nodelay;

limit_req zone=3 burst=200 nodelay;

備註:Nginx 本身並不支持 500r/10s 這種模式,同時,60r/m 的效果等同於 1r/s,因此上述的配置只能起到一個演示的作用。實際上是需要大家通過插件的方式來實現上述的效果。Nginx 官方對於 r/m 的解釋如下:The rate is specified in requests per second (r/s). If a rate of less than one request per second is desired, it is specified in request per minute (r/m). For example, half-request per second is 30r/m.

應用程序和中間件:Istio

對於大部分自研的程序來講,限流功能如有必要,就得自行實現了,算法前面已經介紹過了,當然,也可以參考 Google 的 RateLimiter。

對於進入了 Docker 的程序,則可以考慮引入 Istio,改造成本可控,收益上,限流,熔斷,鑑權等等功能均可具備,但是目前大規模應用的公司還不多,大家都是在慢慢探索中。

對於短期內還無法實現虛擬化的業務,則可以考慮類似 Istio 的模式,在每個程序前,在關鍵程序前,在和多個下游交互的程序前,適度放置 Proxy 來實現流控的效果。在 AWS 的 Proxy-ELB,就是掛在了每組服務之前。這種模式的缺點就在於會略爲增加延時,但如果是同 Regin 部署,那麼只要增加的 Proxy 層級數量可控,整體延時影響也可控。

極少數情況下,也有業務直接通過 Iptable 的方式進行限流,對於物理機的部署模式,筆者只能建議要謹慎使用。

參考文章

分佈式服務限流實戰,已經爲你排好坑了

分佈式系統關注點——限流該怎麼做?

華爲交換機設置配置說明

BFE 開源版本

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章