預案三板斧的限流大法

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

限流能力

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

  • 限制請求數,意思是請求的次數不能太多

Nginx:http://nginx.org/en/docs/http/ngx_http_limit_req_module.html

  • 限制併發連接數,意思是請求的頻率不能太快

Nginx:http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html

  • 限制傳輸速度,意思是請求的體積不能太大

Nginx:http://nginx.org/en/docs/http/ngx_http_core_module.html#limit_rate

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 $uri$ip zone=2:10m rate=100r/s;

limit_req_zone $uri$ip$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開源版本

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